Packages

library(magrittr)
package 㤼㸱magrittr㤼㸲 was built under R version 4.0.3
library(dplyr)
package 㤼㸱dplyr㤼㸲 was built under R version 4.0.3
Attaching package: 㤼㸱dplyr㤼㸲

The following objects are masked from 㤼㸱package:stats㤼㸲:

    filter, lag

The following objects are masked from 㤼㸱package:base㤼㸲:

    intersect, setdiff, setequal, union
library(ggcorrplot)
package 㤼㸱ggcorrplot㤼㸲 was built under R version 4.0.5Loading required package: ggplot2
package 㤼㸱ggplot2㤼㸲 was built under R version 4.0.5Need help? Try Stackoverflow: https://stackoverflow.com/tags/ggplot2
library(ggpubr)
package 㤼㸱ggpubr㤼㸲 was built under R version 4.0.3Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
library(patchwork)
package 㤼㸱patchwork㤼㸲 was built under R version 4.0.5
# library("here")
# library("bookdown")
# library("downloadthis")
library(vegan)
package 㤼㸱vegan㤼㸲 was built under R version 4.0.5Loading required package: permute
Loading required package: lattice
This is vegan 2.5-7
library(plyr)
---------------------------------------------------------------------------------------------------------------------------------------
You have loaded plyr after dplyr - this is likely to cause problems.
If you need functions from both plyr and dplyr, please load plyr first, then dplyr:
library(plyr); library(dplyr)
---------------------------------------------------------------------------------------------------------------------------------------

Attaching package: 㤼㸱plyr㤼㸲

The following object is masked from 㤼㸱package:ggpubr㤼㸲:

    mutate

The following objects are masked from 㤼㸱package:dplyr㤼㸲:

    arrange, count, desc, failwith, id, mutate, rename, summarise, summarize
library(e1071)
package 㤼㸱e1071㤼㸲 was built under R version 4.0.3
library(tidyverse)
package 㤼㸱tidyverse㤼㸲 was built under R version 4.0.3Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
-- Attaching packages ------------------------------------------------------------------------------------------------ tidyverse 1.3.0 --
√ tibble  3.0.4     √ purrr   0.3.4
√ tidyr   1.1.2     √ stringr 1.4.0
√ readr   1.4.0     √ forcats 0.5.0
package 㤼㸱tibble㤼㸲 was built under R version 4.0.3package 㤼㸱tidyr㤼㸲 was built under R version 4.0.3package 㤼㸱readr㤼㸲 was built under R version 4.0.3package 㤼㸱purrr㤼㸲 was built under R version 4.0.5-- Conflicts --------------------------------------------------------------------------------------------------- tidyverse_conflicts() --
x plyr::arrange()    masks dplyr::arrange()
x purrr::compact()   masks plyr::compact()
x plyr::count()      masks dplyr::count()
x tidyr::extract()   masks magrittr::extract()
x plyr::failwith()   masks dplyr::failwith()
x dplyr::filter()    masks stats::filter()
x plyr::id()         masks dplyr::id()
x dplyr::lag()       masks stats::lag()
x plyr::mutate()     masks ggpubr::mutate(), dplyr::mutate()
x plyr::rename()     masks dplyr::rename()
x purrr::set_names() masks magrittr::set_names()
x plyr::summarise()  masks dplyr::summarise()
x plyr::summarize()  masks dplyr::summarize()
library(viridis)
Loading required package: viridisLite
library(GGally)
package 㤼㸱GGally㤼㸲 was built under R version 4.0.5Registered S3 method overwritten by 'GGally':
  method from   
  +.gg   ggplot2
library(ggrepel)
package 㤼㸱ggrepel㤼㸲 was built under R version 4.0.3
library(ggordiplots)
Loading required package: glue

Attaching package: 㤼㸱glue㤼㸲

The following object is masked from 㤼㸱package:dplyr㤼㸲:

    collapse
library(readr)
library(RColorBrewer)
package 㤼㸱RColorBrewer㤼㸲 was built under R version 4.0.5
library(oce) 
package 㤼㸱oce㤼㸲 was built under R version 4.0.5Loading required package: gsw
package 㤼㸱gsw㤼㸲 was built under R version 4.0.5
library(plotly)
package 㤼㸱plotly㤼㸲 was built under R version 4.0.5Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio

Attaching package: 㤼㸱plotly㤼㸲

The following objects are masked from 㤼㸱package:plyr㤼㸲:

    arrange, mutate, rename, summarise

The following object is masked from 㤼㸱package:ggplot2㤼㸲:

    last_plot

The following object is masked from 㤼㸱package:stats㤼㸲:

    filter

The following object is masked from 㤼㸱package:graphics㤼㸲:

    layout
library(purrr)
library(furrr)
Loading required package: future
source("gg_ordisurf_viridis.R")

LOAD OBJECTS IF HAVE RUN ALREADY

geodist_pa<-readRDS(file.path(dataPath,"inputs/SPLITS/DEEP/hiDens_b1500_geodist_pa.rds"))
cannot open compressed file 'C:/Users/a21448/Havforskningsinstituttet/NiN + MAREANO - General/inputs/SPLITS/DEEP/hiDens_b1500_geodist_pa.rds', probable reason 'No such file or directory'Error in gzfile(file, "rb") : cannot open the connection

Data


env_sort <- read.csv(file.path(dataPath,"inputs/SPLITS/DEEP/DeepBelow1500_env_sort_2022-09-30.csv")) %>% as.data.frame
otu_sort <- read.csv(file.path(dataPath,"inputs/SPLITS/DEEP/DeepBelow1500_otu_sort_2022-09-30.csv")) %>% as.data.frame


table(env_sort$SampID2 == otu_sort$SampID)
< table of extent 0 >
#must be same length and equal to unique sample number length - the ordered lists are assumed to be directly relatable on a row by row basis

dim(env_sort)
[1] 190 138
dim(otu_sort)
[1] 190 391

remove any variables with not enough coverage


env_sort <- env_sort %>% select(-c("BO22_lightbotmean_bdmean",
                               "BO22_lightbotltmax_bdmean",
                               "BO22_lightbotltmin_bdmean",
                               "BO22_lightbotrange_bdmean"))
Error in app$vspace(new_style$`margin-top` %||% 0) : 
  attempt to apply non-function

Plot map to check location of samples

env_sort_locations<- ggplot(data = env_sort,
                      aes(x = X.y,
                          y = Y)) +
  theme_classic() +
  geom_point(aes(colour = bathy),
             size = 2) +
  scale_colour_gradient2(low = "red",
                         mid = "yellow",
                         high = "green") +
  ggtitle("Location of samples in sorted env file")

env_sort_locations

Data cleaning

## Removing NAs
otuCompl <- otu_sort[complete.cases(env_sort[, -c(1:which(colnames(env_sort)=="bathy")-1)]), ]
envCompl <- env_sort[complete.cases(env_sort[, -c(1:which(colnames(env_sort)=="bathy")-1)]), ]

## Removing observations with less than 4 OTUs
sel <- rowSums(otuCompl[, -c(1:2)]) >= 4
otuSel <- otuCompl[sel, ]
envSel <- envCompl[sel, ]


## Removing phosphate
#envSel <- envSel %>% select(-phosphate_mean.tif)


dim(otuSel); dim(envSel)
[1] 161 391
[1] 161 134

Show what samples are left after complete cases and >4 OTUs filters

env_sel_locations<- ggplot(data = envSel,
                      aes(x = X.y,
                          y = Y)) +
  theme_classic() +
  geom_point(aes(colour = bathy),
             size = 2) +
  scale_colour_gradient2(low = "red",
                         mid = "yellow",
                         high = "green") +
  ggtitle("Location of samples in sorted env file")

env_sel_locations

Show what got removed in complete cases filter

envCompl_ccrem<-env_sort%>%filter(!SampID%in%envCompl$SampID)

envCompl_ccrem_locations<- ggplot(data = envCompl_ccrem,
                      aes(x = X.y,
                          y = Y)) +
  theme_classic() +
  geom_point(aes(colour = bathy),
             size = 2) +
  scale_colour_gradient2(low = "red",
                         mid = "yellow",
                         high = "green") +
  ggtitle("Location of removed complete case samples resulting in envSel file")

envCompl_ccrem_locations

Show what got removed in <4 OTUs filter

inv.sel <- rowSums(otuCompl[, -c(1:2)]) < 4
env.invSel <- envCompl[inv.sel, ]

env.invSel_locations<- ggplot(data = env.invSel,
                      aes(x = X.y,
                          y = Y)) +
  theme_classic() +
  geom_point(aes(colour = bathy),
             size = 2) +
  scale_colour_gradient2(low = "red",
                         mid = "yellow",
                         high = "green") +
  ggtitle("Location of removed samples with <4 OTUs resulting in envSel file")

env.invSel_locations

[Optional] Data thinning

# otu_red <- otu1[1:500, ]
# otu <- otu_red
# 
# env_red <- env[1:500, ]
# env <- env_red

Whole dataset for first run

otu<-otuSel[,-c(1:which(colnames(otuSel)=="Acesta_excavata")-1)]
env<-envSel[, -c(2:(which(colnames(env_sort)=="bathy")-1))]

table(is.na(otu))

FALSE 
62629 
table(is.na(env))

FALSE 
18837 
#str(otuSel)
#str(envSel)

Splitting in subsets

# ## Class 1
# otu1 <- subset(otuSel, envSel$SplitRev == 1)
# env1 <- envSel %>% filter(SplitRev == 1)
# 
# ## Class 2
# otu2 <- subset(otuSel, envSel$SplitRev == 2)
# env2 <- envSel %>% filter(SplitRev == 2)
# 
# ## Class 4
# otu3 <- subset(otuSel, envSel$SplitRev == 3)
# env3 <- envSel %>% filter(SplitRev == 3)
# 
# ## Class 4
# otu4 <- subset(otuSel, envSel$SplitRev == 4)
# env4 <- envSel %>% filter(SplitRev == 4)
# 
# ## Class 6
# otu6 <- subset(otuSel, envSel$SplitRev == 6)
# env6 <- envSel %>% filter(SplitRev == 6)
# 
# ## Class 7
# otu7 <- subset(otuSel, envSel$SplitRev == 7)
# env7 <- envSel %>% filter(SplitRev == 7)
# 
# ## Class 8
# otu8 <- subset(otuSel, envSel$SplitRev == 8)
# env8 <- envSel %>% filter(SplitRev == 8)

Selecting subset

# ## Selecting 
# otu <- otu2
# env <- env2

format data correctly

#env$coords.x1<-as.numeric(env$coords.x1)
#env$coords.x2<-as.numeric(env$coords.x2)

Abundance weighting

Make function You might have to drop variables that have been imported as character


otu_pa <- decostand(x = otu[, -c(1)],
                    method = "pa")

# y = ax^w         # power transformation formula
dt <- otu[, -c(1:2)]          # species data to transform
x_mn <- min(dt[dt > 0])
x_mx <- max(dt)
rng <- 6           # abundance range
w <- log(rng) / (log(x_mx) - log(x_mn))
a <- x_mn^(-w)
# otu_6 <- a * dt[, -c(1:3)]^w
otu_6 <- a * dt^w
range(otu_6)
[1] 0 6

Ordination methods

DCA PA

Sys.time()
[1] "2022-10-03 12:14:10 CEST"
dca_pa <- decorana(veg = otu_pa)
some species were removed because they were missing in the data
print(dca_pa, head=T)

Call:
decorana(veg = otu_pa) 

Detrended correspondence analysis with 26 segments.
Rescaling of axes with 4 iterations.

                  DCA1   DCA2   DCA3   DCA4
Eigenvalues     0.4412 0.3659 0.2158 0.3282
Decorana values 0.4811 0.4035 0.3339 0.3026
Axis lengths    4.3022 3.5983 3.3305 4.2530

GNMDS PA

Distances - don’t run if loading saved object

## Bray-Curtis
dist_pa <- vegdist(x = otu_pa, method = "bray")

## Geodist
ep <- 0.8     # epsilon
geodist_pa <- isomapdist(dist = dist_pa, epsilon = ep)
Save the result - don’t run if loading saved object
saveRDS(geodist_pa,
        file = (file.path(dataPath,"inputs/SPLITS/DEEP/hiDens_b1500_b1500_geodist_pa.rds")))

Ordination

monoMDS - don’t run if loading saved object

took 6.4mins with jonatan’s paralell solution (usually ~10mins)

Using old version without furrr


 Sys.time()
[1] "2022-09-30 15:06:20 CEST"
 d <- 2
 mds_pa <- list()

 for (i in 1:100) {
   mds_pa[[i]]<-monoMDS(geodist_pa,
                     matrix(c(runif(dim(otu_pa)[1]*d)),
                            nrow = dim(otu_pa)[1]),
                     k = d,
                     model = "global",
                     maxit = 2000,
                     smin = 1e-7,
                     sfgrmin = 1e-7)
 }
 Sys.time()
[1] "2022-09-30 15:06:33 CEST"
# # monoMDS
#  d <- 2
# mds_r6 <- list()
# 
#  Sys.time()
#  for (i in 1:200) {
#    mds_r6[[i]]<-monoMDS(geodist_r6,
#                      matrix(c(runif(dim(otu_6)[1]*d)),
#                             nrow = dim(otu_6)[1]),
#                      k = d,
#                      model = "global",
#                      maxit = 2000,
#                      smin = 1e-7,
#                      sfgrmin = 1e-7)# }
#  Sys.time()

# #Jonatan's upgrade for speed - parallel processing of mds reptitions
# library(furrr) # Package to run non sequential functions in parallel
# #library(purrr)
# plan(multisession)
# 
# d <- 2
# 
# i <-200 # number of reps
# 
# List_geodist_pa <- lapply(seq_len(i), function(X) geodist_pa) # makes a list with the input data repeated as many times as reps wanted
# start_t<-Sys.time()
# Xmds_pa<-furrr::future_map(List_geodist_pa, 
#                   function(x) monoMDS(x,
#                                       matrix(c(runif(dim(otu_6)[1]*d)),
#                                              nrow = dim(otu_6)[1]),
#                                       k = d,
#                                       model = "global",
#                                       maxit = 2000,
#                                       smin = 1e-7,
#                                       sfgrmin = 1e-7),
#                   .progress = TRUE)
# Sys.time() - start_t
Save the result - don’t run if loading saved object

can take a few mins

saveRDS(mds_pa,
        file = file.path(dataPath,"inputs/SPLITS/DEEP/hiDens_b1500_mds_pa.rds"))
Best nmds solution - PA

Make function?

# Loading geodist object
# geodist_nfi <- readRDS(file = "../SplitRev2_geodist_pa_full.rds")
# Loading mds results
# mds <- readRDS(file = "../SplitRev2_mds_pa_full.rds")

## Extracting the stress of each nmds iteration
mds_stress_pa<-unlist(lapply(mds_pa, function(v){v[[22]]})) 

ordered_pa <-order(mds_stress_pa)

## Best, second best, and worst solution
mds_stress_pa[ordered_pa[1]]
[1] 0.2601212
mds_stress_pa[ordered_pa[2]]
[1] 0.2603306
mds_stress_pa[ordered_pa[10]]
[1] 0.2608846
## Scaling of axes to half change units and varimax rotation by postMDS
mds_best_pa<-postMDS(mds_pa[[ordered_pa[1]]],
                  geodist_pa, 
                  pc = TRUE, 
                  halfchange = TRUE, 
                  threshold = ep)     # Is this threshold related to the epsilon above?
mds_best_pa

Call:
monoMDS(dist = geodist_pa, y = matrix(c(runif(dim(otu_pa)[1] *      d)), nrow = dim(otu_pa)[1]), k = d, model = "global", maxit = 2000,      smin = 1e-07, sfgrmin = 1e-07) 

Non-metric Multidimensional Scaling

161 points, dissimilarity ‘bray shortest isomap’, call ‘isomapdist(dist = dist_pa, epsilon = ep)’

Dimensions: 2 
Stress:     0.2601212 
Stress type 1, weak ties
Scores scaled to unit root mean square, rotated to principal components
Stopped after 104 iterations: Stress nearly unchanged (ratio > sratmax)
mds_secbest_pa <- postMDS(mds_pa[[ordered_pa[2]]],
                          geodist_pa, 
                          pc = TRUE, 
                          halfchange = TRUE, 
                          threshold = ep)
mds_secbest_pa

Call:
monoMDS(dist = geodist_pa, y = matrix(c(runif(dim(otu_pa)[1] *      d)), nrow = dim(otu_pa)[1]), k = d, model = "global", maxit = 2000,      smin = 1e-07, sfgrmin = 1e-07) 

Non-metric Multidimensional Scaling

161 points, dissimilarity ‘bray shortest isomap’, call ‘isomapdist(dist = dist_pa, epsilon = ep)’

Dimensions: 2 
Stress:     0.2603306 
Stress type 1, weak ties
Scores scaled to unit root mean square, rotated to principal components
Stopped after 248 iterations: Stress nearly unchanged (ratio > sratmax)
## Procrustes comparisons
procr_pa <- procrustes(mds_best_pa,
                    mds_secbest_pa,
                    permutations=999)
protest(mds_best_pa,
        mds_secbest_pa,
        permutations=999)

Call:
protest(X = mds_best_pa, Y = mds_secbest_pa, permutations = 999) 

Procrustes Sum of Squares (m12 squared):        0.1237 
Correlation in a symmetric Procrustes rotation: 0.9361 
Significance:  0.001 

Permutation: free
Number of permutations: 999
plot(procr_pa)

png(file=file.path(dataPath,"outputs/hiDens_b1500_procrustes_pa.png"), width=1000, height=700)
plot(procr_pa)
dev.off()
png 
  2 

Correlation of axis: DCA vs NMDS - PA
# Extracting ordination axis
ax <- 2
axis_pa <- cbind(mds_best_pa$points,
                 scores(dca_pa,
                        display = "sites",
                        origin = TRUE)[, 1:ax])

ggcorr(axis_pa, 
       method=c("everything","kendall"), 
       label = TRUE,
       label_size = 3, 
       label_color = "black",  
       nbreaks = 8,
       label_round = 3,
       low = "red",
       mid = "white",
       high = "green")

NA
NA
Save the figure
ggsave(filename = file.path(dataPath,"outputs/hiDens_b1500_correlationPCAvsNMDS_PA.png"),
       device = "png",
       dpi=300 )
Saving 7 x 7 in image
# Switching direction of NMDS1
# mds_best$points[, 1] <- -mds_best$points[, 1]

DCA R6

dca_r6 <- decorana(veg = otu_6)
some species were removed because they were missing in the data
print(dca_r6, head=T)

Call:
decorana(veg = otu_6) 

Detrended correspondence analysis with 26 segments.
Rescaling of axes with 4 iterations.

                  DCA1   DCA2   DCA3   DCA4
Eigenvalues     0.4624 0.3966 0.3092 0.3106
Decorana values 0.5075 0.4233 0.3731 0.2867
Axis lengths    4.3845 4.1435 3.1853 3.4650

GNMDS R6

Distances - don’t run if loading saved object

## Bray-Curtis
dist_r6 <- vegdist(x = otu_6, method = "bray")

## Geodist
ep <- 0.80     # epsilon
geodist_r6 <- isomapdist(dist = dist_r6, epsilon = ep)
Save the result - don’t run if loading saved object
saveRDS(geodist_r6,
        file = (file.path(dataPath,"inputs/SPLITS/DEEP/hiDens_b1500_geodist_r6.rds")))

Ordination

200 reps - don’t run if loading saved object

(Jonatan’s parallel solution took 5.4mins, before it took 10mins)

Using older version without furrr.

## monoMDS
d <- 2
mds_r6 <- list()

Sys.time()
[1] "2022-09-30 15:07:13 CEST"
for (i in 1:200) {
  mds_r6[[i]]<-monoMDS(geodist_r6,
                    matrix(c(runif(dim(otu_6)[1]*d)),
                           nrow = dim(otu_6)[1]),
                    k = d,
                    model = "global",
                    maxit = 2000,
                    smin = 1e-7,
                    sfgrmin = 1e-7)
}
Sys.time()
[1] "2022-09-30 15:07:28 CEST"
# #Jonatan's upgrade for speed - parallel processing of mds reptitions
# #library(furrr) # Package to run non sequential functions in parallel
# #library(purrr)
# plan(multisession)
# 
# d <- 2
# 
# i <-200 # number of reps
# 
# List_geodist_r6 <- lapply(seq_len(i), function(X) geodist_r6) # makes a list with the input data repeated as many times as reps wanted
# start_t<-Sys.time()
# Xmds_r6<-furrr::future_map(List_geodist_r6, 
#                   function(x) monoMDS(x,
#                                       matrix(c(runif(dim(otu_6)[1]*d)),
#                                              nrow = dim(otu_6)[1]),
#                                       k = d,
#                                       model = "global",
#                                       maxit = 2000,
#                                       smin = 1e-7,
#                                       sfgrmin = 1e-7),
#                   .progress = TRUE)
# Sys.time() - start_t
Save the result - don’t run if loading saved object
saveRDS(mds_r6,
        file = file.path(dataPath,"inputs/SPLITS/DEEP/hiDens_b1500_mds_r6.rds")) 
Best nmds solution r6 200 rep
# Loading geodist object
# geodist_nfi <- readRDS(file = "../SplitRev2_geodist_nfi.rds")

# Loading mds results
# mds <- readRDS(file = "../SplitRev2_mds.rds")

## Extracting the stress of each nmds iteration
mds_stress_r6<-unlist(lapply(mds_r6, function(v){v[[22]]})) 

ordered_r6 <-order(mds_stress_r6)

## Best, second best, and worst solution
mds_stress_r6[ordered_r6[1]]
[1] 0.2524937
mds_stress_r6[ordered_r6[2]]
[1] 0.2525011
mds_stress_r6[ordered_r6[100]]
[1] 0.2581464
## Scaling of axes to half change units and varimax rotation by postMDS
mds_best_r6<-postMDS(mds_r6[[ordered_r6[1]]],
                     geodist_r6, 
                     pc = TRUE, 
                     halfchange = TRUE, 
                     threshold = ep)     # Is this threshold related to the epsilon above?
mds_best_r6

Call:
monoMDS(dist = geodist_r6, y = matrix(c(runif(dim(otu_6)[1] *      d)), nrow = dim(otu_6)[1]), k = d, model = "global", maxit = 2000,      smin = 1e-07, sfgrmin = 1e-07) 

Non-metric Multidimensional Scaling

161 points, dissimilarity ‘bray shortest isomap’, call ‘isomapdist(dist = dist_r6, epsilon = ep)’

Dimensions: 2 
Stress:     0.2524937 
Stress type 1, weak ties
Scores scaled to unit root mean square, rotated to principal components
Stopped after 70 iterations: Stress nearly unchanged (ratio > sratmax)
mds_secbest_r6<-postMDS(mds_r6[[ordered_r6[2]]],
                        geodist_r6, 
                        pc = TRUE, 
                        halfchange = TRUE, 
                        threshold = ep)
mds_secbest_r6

Call:
monoMDS(dist = geodist_r6, y = matrix(c(runif(dim(otu_6)[1] *      d)), nrow = dim(otu_6)[1]), k = d, model = "global", maxit = 2000,      smin = 1e-07, sfgrmin = 1e-07) 

Non-metric Multidimensional Scaling

161 points, dissimilarity ‘bray shortest isomap’, call ‘isomapdist(dist = dist_r6, epsilon = ep)’

Dimensions: 2 
Stress:     0.2525011 
Stress type 1, weak ties
Scores scaled to unit root mean square, rotated to principal components
Stopped after 118 iterations: Stress nearly unchanged (ratio > sratmax)
## Procrustes comparisons
procr_r6 <- procrustes(mds_best_r6,
                       mds_secbest_r6,
                       permutations=999)
protest(mds_best_r6,
        mds_secbest_r6,
        permutations=999)

Call:
protest(X = mds_best_r6, Y = mds_secbest_r6, permutations = 999) 

Procrustes Sum of Squares (m12 squared):        0.000444 
Correlation in a symmetric Procrustes rotation: 0.9998 
Significance:  0.001 

Permutation: free
Number of permutations: 999
plot(procr_r6)

png(file.path(dataPath,"outputs/hiDens_b1500_procrustes_r6.png"), width=1000, height=700,) #added 1000
plot(procr_r6)
dev.off()
png 
  2 

#### 1000 reps - don’t run if loading saved object Currently commented out as it gained nothing but took extra time. Can be removed in due course.

Correlation of axis: DCA vs NMDS

retain the 200 rep version

# Extracting ordination axis
ax <- 2
axis_r6 <- cbind(mds_best_r6$points,
                 scores(dca_r6,
                        display = "sites",
                        origin = TRUE)[, 1:ax])

ggcorr(axis_r6, 
       method=c("everything","kendall"), 
       label = TRUE,
       label_size = 3, 
       label_color = "black",  
       nbreaks = 8,
       label_round = 3,
       low = "red",
       mid = "white",
       high = "green")

Save the figure
ggsave(filename = file.path(dataPath,"outputs/hiDens_b1500_correlationPCAvsNMDS_r6.png"),
       device = "png",
       dpi=300 )
Saving 7 x 7 in image
# Switching direction of NMDS1
# mds_best$points[, 1] <- -mds_best$points[, 1]

Plotting DCA & GNMDS

## Adding scores to data frame
otu_6$gnmds1 <- mds_best_r6$points[, 1]
otu_6$gnmds2 <- mds_best_r6$points[, 2]
otu_6$dca1 <- scores(dca_r6, display = "sites", origin = TRUE)[, 1]
otu_6$dca2 <- scores(dca_r6, display = "sites", origin = TRUE)[, 2]

p_gnmds_r6 <- ggplot(data = otu_6,
                     aes(x = gnmds1,
                         y = gnmds2)) +
  theme_classic() +
  coord_fixed() +
  ggtitle("GNMDS",
          subtitle = "First run") +
  geom_point(colour = "red") +
  geom_vline(xintercept = 0,
             linetype = 2,
             colour = "lightgray") +
  geom_hline(yintercept = 0,
             linetype = 2,
             colour = "lightgray")

p_dca_r6 <- ggplot(data = otu_6,
                   aes(x = dca1,
                       y = dca2)) +
  theme_classic() +
  coord_fixed() +
  ggtitle("DCA",
          subtitle = "First run") +
  geom_point(colour = "red") +
  geom_vline(xintercept = 0,
             linetype = 2,
             colour = "lightgray") +
  geom_hline(yintercept = 0,
             linetype = 2,
             colour = "lightgray")

p_gnmds_r6 + p_dca_r6

NA
NA
Save the figure
ggsave(file.path(dataPath,"outputs/hiDens_b1500_gnmds_dca_r6.png"),
       device = "png", 
       dpi=300)
Saving 7 x 7 in image

Species-environment relationships

Selecting ordination

ord <- mds_best_r6

## Axis scores if selected ord is GNMDS
axis <- ord$points %>% as.data.frame

## Axis scores if selected ord is DCA
# axis <- scores(ord,
#                display = "sites",
#                origin = TRUE)[, 1:ax])

Create additional variables

decided to make MLD-bathy vars

env<-env %>% 
  mutate ("MLDmean_bathy"=MLDmean_Robinson-(bathy*-1),
          "MLDmin_bathy"=MLDmin_Robinson-(bathy*-1),
          "MLDmax_bathy"=MLDmax_Robinson-(bathy*-1))

env$MLDmean_bathy<-cut(env$MLDmean_bathy, 
      breaks=c(-2560, -20,20,130),#checked range of values first (min -2554, max 123)
      labels=c('belowMLD','onPycno','inMixLayer'))
env$MLDmin_bathy<-cut(env$MLDmin_bathy, 
      breaks=c(-2560, -20,20,130),#checked range of values first (min -2554, max 123)
      labels=c('belowMLD','onPycno','inMixLayer'))
env$MLDmax_bathy<-cut(env$MLDmax_bathy, 
      breaks=c(-2560, -20,20,130),#checked range of values first (min -2554, max 123)
      labels=c('belowMLD','onPycno','inMixLayer'))

env$swDensRob_avs<-swRho(salinity=env$Smean_Robinson,
                         temperature=env$Tmean_Robinson,
                         pressure=(env$bathy*-1),
                         eos="unesco")

Correlation ordination axes and environmental variables

Removing non-env vars

env_cont<-env%>% select(-c(landscape,sedclass,gmorph, MLDmean_bathy, MLDmax_bathy, MLDmin_bathy, #categorical
                           optional, #not a var
                           MLDmax_Robinson, MLDmean_Robinson, MLDmin_Robinson,  #replaced by new vars
                           MLDsd_Robinson #not meaningful
                           ))
env_cont<-env_cont%>% mutate_if(is.integer,as.numeric)
env_corr <- env_cont #%>% select(-c(SampID))

# env_corr$coords.x1<-as.numeric(env_corr$coords.x1)
# env_corr$coords.x2<-as.numeric(env_corr$coords.x2)

env_corr[(!is.numeric(env_corr)),]

Correlations

# Vector to hold correlations
cor_ax1 <- NULL
cor_ax2 <- NULL
pv_ax1 <- NULL
pv_ax2 <- NULL

# NMDS1
for( i in seq(length(env_corr))) {
  ct.i <- cor.test(axis$MDS1,
                   env_corr[, i],
                   method = "kendall")
  cor_ax1[i] <- ct.i$estimate
  pv_ax1[i] <- ct.i$p.value
}
the standard deviation is zerothe standard deviation is zerothe standard deviation is zerothe standard deviation is zerothe standard deviation is zerothe standard deviation is zero
# NMDS2
for( i in seq(length(env_corr))) {
  ct.i <- cor.test(axis$MDS2,
                   env_corr[, i],
                   method = "kendall")
  cor_ax2[i] <- ct.i$estimate
  pv_ax2[i] <- ct.i$p.value
}
the standard deviation is zerothe standard deviation is zerothe standard deviation is zerothe standard deviation is zerothe standard deviation is zerothe standard deviation is zero
cor_tab <- data.frame(env = names(env_corr),
                      ord_ax1 = cor_ax1,
                      pval_ax1 = pv_ax1,
                      ord_ax2 = cor_ax2,
                      pval_ax2 = pv_ax2)

cor_tab

write.csv(x = cor_tab,
          file = file.path(dataPath,"inputs/SPLITS/DEEP/hiDens_b1500_cor-table_r6_200rep_MLD-bathy.csv"),
          row.names = FALSE)

Dot chart to check for gaps in correlation

cor_a1_sort<-cor_tab%>%
There were 15 warnings (use warnings() to see them)
  mutate(abs_ord_ax1=abs(ord_ax1),
         abs_ord_ax2=abs(ord_ax2)) %>%
  arrange(desc(abs_ord_ax1))

cor_a2_sort<-cor_tab%>%
  mutate(abs_ord_ax1=abs(ord_ax1),
         abs_ord_ax2=abs(ord_ax2)) %>%
  arrange(desc(abs_ord_ax2))

dotchart(cor_a1_sort$abs_ord_ax1, main="Absolute (+/-) correlations between envVars and gnmds axis 1")


cor_cut<-0.50 #decide

cor_sel<-subset(cor_a1_sort,abs_ord_ax1>cor_cut)
cor_sel

as.data.frame(cor_sel$env)

Sel env var (top corr)

env_os <- env[, cor_sel$env]
env_os
str(env_os)
'data.frame':   161 obs. of  11 variables:
 $ swDensRob_avs               : num  1037 1037 1036 1036 1035 ...
 $ bathy                       : num  -1961 -2009 -1598 -1596 -1594 ...
 $ Tmean_Robinson              : num  -0.246 -0.251 0.45 0.528 0.607 ...
 $ Smax_Robinson               : num  35 34.9 35 35 35 ...
 $ Tmax_Robinson               : num  -0.234 -0.234 0.498 0.579 0.66 ...
 $ Smean_Robinson              : num  34.9 34.9 35 35 35 ...
 $ Tmin_Robinson               : num  -0.271 -0.299 0.382 0.458 0.534 ...
 $ Smin_Robinson               : num  34.9 34.9 35 35 35 ...
 $ BO22_carbonphytoltmin_bdmean: num  0.0199 0.0199 0.0197 0.0197 0.0197 ...
 $ BO22_ironmean_bdmean        : num  0.000684 0.000687 0.000674 0.000673 0.000673 ...
 $ BO22_ironrange_bdmean       : num  0.000684 0.000687 0.000674 0.000673 0.000673 ...

Ordisurfs top corr

ordsrfs <- list(length = ncol(env_os))

for (i in seq(ncol(env_os))) {
  os.i <- gg_ordisurf(ord = ord,
                      env.var = env_os[, i],
                      pt.size = 1,
                      # binwidth = 0.05,
                      var.label = names(env_os)[i],
                      gen.text.size = 10,
                      title.text.size = 15,
                      leg.text.size = 10
                      )
  
  ordsrfs[[i]] <- os.i$plot
}


ordsrfs_plt <- ggarrange(plotlist = ordsrfs,
                         nrow = 4,
                         ncol = 3)

ordsrfs_plt

Save some outputs
ggexport(ordsrfs_plt,
          filename = file.path(dataPath,"outputs/hiDens_b1500_ordisurfs_top_corr.png"),
          width = 1000,
          height = 1000)

Sel env var (manual)

env_os_m <- env[,c("Tmean_Robinson", #top corr
                  "salt_max", #top corr
                  "Smax_Robinson", #comparison to top corr
                  "swDensRob_avs", #top corr
                  "BO22_icecoverltmax_ss",#top corr ax2
                  "BO22_icecovermean_ss",#top corr
                  "BO22_dissoxmean_bdmean",#top corr
                  #"BO22_carbonphytoltmin_bdmean",#top corr - no clear gradient in ordisurf
                  "BO22_ppltmin_ss", #top corr
                  "X.y", #comparison to Y
                  "Y", #top corr
                  "spd_std", #top corr ax2 (blended model)
                  "CSpdsd_Robinson", #comparison to top corr ax2 (blended model)
                  "mud", #highest sed var ax1 + corr
                  "gravel",#highest sed var ax1 - corr
                  "BO22_silicateltmax_bdmean", #just under top corr ax1
                  "bathy", #intuitive for comparisons
                  "slope9"#intuitive for comparisons
                )]
env_os_m
str(env_os_m)
'data.frame':   161 obs. of  17 variables:
 $ Tmean_Robinson           : num  -0.246 -0.251 0.45 0.528 0.607 ...
 $ salt_max                 : num  34.9 34.9 35 35 35 ...
 $ Smax_Robinson            : num  35 34.9 35 35 35 ...
 $ swDensRob_avs            : num  1037 1037 1036 1036 1035 ...
 $ BO22_icecoverltmax_ss    : num  9.78e-05 9.71e-05 0.00 0.00 0.00 ...
 $ BO22_icecovermean_ss     : num  1.22e-05 1.25e-05 0.00 0.00 0.00 ...
 $ BO22_dissoxmean_bdmean   : num  304 304 293 293 293 ...
 $ BO22_ppltmin_ss          : num  0.00 0.00 1.49e-05 1.49e-05 1.49e-05 ...
 $ X.y                      : num  463061 470861 554861 555061 555261 ...
 $ Y                        : num  8311934 8295534 7744134 7744134 7744134 ...
 $ spd_std                  : num  0.0511 0.0508 0.0726 0.0736 0.0748 ...
 $ CSpdsd_Robinson          : num  0.014566 0.01478 0.000513 0.000492 0.00047 ...
 $ mud                      : num  96 96 69.5 69.5 69.5 0 0 80 0 0 ...
 $ gravel                   : num  0 0 0.5 0.5 0.5 0 0 0 0 0 ...
 $ BO22_silicateltmax_bdmean: num  13.2 13.4 10.8 10.8 10.8 ...
 $ bathy                    : num  -1961 -2009 -1598 -1596 -1594 ...
 $ slope9                   : num  0.814 0.672 3.516 3.255 3.39 ...

Ordisurfs manually selected

ordsrfs_m <- list(length = ncol(env_os_m))

for (i in seq(ncol(env_os_m))) {
  os.i_m <- gg_ordisurf(ord = ord,
                      env.var = env_os_m[, i],
                      pt.size = 1,
                      # binwidth = 0.05,
                      var.label = names(env_os_m)[i],
                      gen.text.size = 10,
                      title.text.size = 15,
                      leg.text.size = 10)
  
  ordsrfs_m[[i]] <- os.i_m$plot
}


ordsrfs_plt_m <- ggarrange(plotlist = ordsrfs_m,
                         nrow = 6,
                         ncol = 3)

ordsrfs_plt_m

Save some outputs
ggexport(ordsrfs_plt_m,
          filename = file.path(dataPath,"outputs/hiDens_b1500_ordisurfs_man_sel_domean.png"),
          width = 2000,
          height = 2000)

Envfit

## Select if any var should be excluded from envfit (makes less busy to read)
env_os_m_envfit<-env_os_m [,c("Tmean_Robinson", #top corr
                  "salt_max", #top corr
                  "Smax_Robinson", #comparison to top corr
                  "swDensRob_avs", #top corr
                  "BO22_icecoverltmax_ss",#top corr ax2
                  #"BO22_icecovermean_ss",#top corr
                  "BO22_dissoxmean_bdmean",#top corr
                  #"BO22_dissoxltmin_bdmean",#top corr
                  #"BO22_carbonphytoltmin_bdmean",#top corr - no clear gradient in ordisurf
                  "BO22_ppltmin_ss", #top corr
                  "X.y", #comparison to Y
                  "Y", #top corr
                  "spd_std", #top corr ax2 (blended model)
                 # "CSpdsd_Robinson", #comparison to top corr ax2 (blended model)
                  "mud", #highest sed var ax1 + corr
                  "gravel",#highest sed var ax1 - corr
                  "BO22_silicateltmax_bdmean", #just under top corr ax1
                  "bathy" #intuitive for comparisons
                
  
)]

colnames(env_os_m_envfit)<-c("T", #top corr
                  "sMx", #top corr
                  "SmaxR", #comparison to top corr
                  "swDensR", #top corr
                  "icecovmax",#top corr ax2
                  #"icecovav",#top corr
                  "dissoxav",#top corr
                  #"dissoxmin",#top corr
                  #"BO22_carbonphytoltmin_bdmean",#top corr - no clear gradient in ordisurf
                  "ppltmin", #top corr
                  "X", #comparison to Y
                  "Y", #top corr
                  "spd_std", #top corr ax2 (blended model)
                 # "CSsd", #comparison to top corr ax2 (blended model)
                  "mud", #highest sed var ax1 + corr
                  "gravel",#highest sed var ax1 - corr
                  "SiLtmax", #just under top corr ax1
                  "bathy" #intuitive for comparisons
                 )

## Envfot plot
gg_envfit(ord = ord,
          env = env_os_m_envfit,
          pt.size = 1)


## Envfit analysis

ef <- envfit(ord = ord,
             env = env_os_m_envfit,
             # na.rm = TRUE
             )
efDF <- as.data.frame(scores(ef,
                             display = "vectors"))
Save the plot
ggsave(filename = file.path(dataPath,"outputs/hiDens_b1500_EnvFit_man_sel_cln_domean.png"),
       device = "png",
       dpi=300 )
Saving 7 x 7 in image

Categorical envVar visualised on the mdsplots

use dataset with categorical var included

env_vis<-env
env_vis$gnmds1 <- otu_6$gnmds1
env_vis$gnmds2 <- otu_6$gnmds2

env_vis$dca1 <- otu_6$dca1
env_vis$dca2 <- otu_6$dca2

env_vis$SampID <- envSel$SampID

gnmds w mld mean - bathy

dca_int <- ggplot(data = env_vis,
                     aes(x = dca1,
                         y = dca2)) +
  theme_classic() +
  coord_fixed() +
  ggtitle("Interactive DCA sample ID plot",
          subtitle = "First run") +
  geom_point(aes(colour = factor(SampID))) +
  geom_vline(xintercept = 0,
             linetype = 2,
             colour = "lightgray") +
  geom_hline(yintercept = 0,
             linetype = 2,
             colour = "lightgray")

ggplotly(dca_int)

gnmds w mld mean - bathy

p_mld <- ggplot(data = env_vis,
                     aes(x = gnmds1,
                         y = gnmds2)) +
  theme_classic() +
  coord_fixed() +
  ggtitle("GNMDS coloured by proximity to mixed layer depth",
          subtitle = "First run") +
  geom_point(aes(colour = MLDmean_bathy)) +
  geom_vline(xintercept = 0,
             linetype = 2,
             colour = "lightgray") +
  geom_hline(yintercept = 0,
             linetype = 2,
             colour = "lightgray")

p_mld

gnmds w sedclass

deal with sedclass codes
env_vis<- env_vis %>% 
  mutate(
    sedclassName = case_when(
            sedclass == "1" ~ "SedCoverR",
            sedclass == "5" ~ "Rock",
            sedclass == "20" ~ "Mud",
            sedclass == "21" ~ "MwBlock",
            sedclass == "40" ~ "sMud",
            sedclass == "80" ~ "mSand",
            sedclass == "100" ~ "Sand",
            sedclass == "110" ~ "gMud",
            sedclass == "115" ~ "gsMud",
            sedclass == "120" ~ "gmSand",
            sedclass == "130" ~ "gSand",
            sedclass == "150" ~ "MSG",
            sedclass == "160" ~ "sGravel",
            sedclass == "170" ~ "Gravel",
            sedclass == "175" ~ "GravBlock",
            sedclass == "185" ~ "SGBmix",
            sedclass == "205" ~ "S/MwB",
            sedclass == "206" ~ "S/MwG/B",
            sedclass == "215" ~ "SGBalt",
            sedclass == "300" ~ "HardSed",
            sedclass == "500" ~ "Biogenic"
                        
    )
  )
colour palette to cope with up to 25 categorical colours
c25 <- c(
  "dodgerblue2", "#E31A1C", # red
  "green4",
  "#6A3D9A", # purple
  "#FF7F00", # orange
  "black", "gold1",
  "skyblue2", "#FB9A99", # lt pink
  "palegreen2",
  "#CAB2D6", # lt purple
  "#FDBF6F", # lt orange
  "gray70", "khaki2",
  "maroon", "orchid1", "hiDens_b1500pink1", "blue1", "steelblue4",
  "darkturquoise", "green1", "yellow4", "yellow3",
  "darkorange4", "brown"
)

p_sed <- ggplot(data = env_vis,
                     aes(x = gnmds1,
                         y = gnmds2)) +
  theme_classic() +
  coord_fixed() +
  ggtitle("GNMDS coloured by sediment class",
          subtitle = "First run") +
  geom_point(aes(colour = factor(sedclassName))) +
   scale_colour_manual(values=c25)+
  geom_vline(xintercept = 0,
             linetype = 2,
             colour = "lightgray") +
  geom_hline(yintercept = 0,
             linetype = 2,
             colour = "lightgray")+
  guides(colour=guide_legend(ncol=2))

p_sed

gnmds w lanscape

deal with sedclass codes
env_vis<- env_vis %>% 
  mutate(
    landscapeName = case_when(
            landscape == "1" ~ "Strandflat",
            landscape == "21" ~ "ContSlope",
            landscape == "22" ~ "Canyon",
            landscape == "31" ~ "Valley",
            landscape == "32" ~ "Fjord",
            landscape == "41" ~ "DeepSeaPlane",
            landscape == "42" ~ "SlopePlain",
            landscape == "43" ~ "ShelfPlain",
            landscape == "431" ~ "shallowValley"
    )
  )

p_land <- ggplot(data = env_vis,
                     aes(x = gnmds1,
                         y = gnmds2)) +
  theme_classic() +
  coord_fixed() +
  ggtitle("GNMDS coloured by landscape class",
          subtitle = "First run") +
  geom_point(aes(colour = factor(landscapeName))) +
  geom_vline(xintercept = 0,
             linetype = 2,
             colour = "lightgray") +
  geom_hline(yintercept = 0,
             linetype = 2,
             colour = "lightgray")+
  guides(colour=guide_legend(ncol=2))

p_land

Gnmds w gmorph


p_gmo <- ggplot(data = env_vis,
                     aes(x = gnmds1,
                         y = gnmds2)) +
  theme_classic() +
  coord_fixed() +
  ggtitle("GNMDS coloured by landscape class",
          subtitle = "First run") +
  geom_point(aes(colour = factor(gmorph))) +
  geom_vline(xintercept = 0,
             linetype = 2,
             colour = "lightgray") +
  geom_hline(yintercept = 0,
             linetype = 2,
             colour = "lightgray")+
  guides(colour=guide_legend(ncol=2))

p_gmo

cat_var_plots<-p_mld+p_sed+p_land+p_gmo
Save the plot
ggexport(cat_var_plots,
          filename = file.path(dataPath,"outputs/hiDens_b1500_gnmds_catvar.png"),
          width = 1000,
          height = 800)

Sample identification in the mdsplot

p_gmo <- ggplot(data = env_vis,
                     aes(x = gnmds1,
                         y = gnmds2)) +
  theme_classic() +
  coord_fixed() +
  ggtitle("GNMDS coloured by sample",
          subtitle = "First run") +
  geom_point(aes(colour = factor(SampID))) +
  geom_vline(xintercept = 0,
             linetype = 2,
             colour = "lightgray") +
  geom_hline(yintercept = 0,
             linetype = 2,
             colour = "lightgray")+
  guides(colour="none", size="none")

ggplotly(p_gmo)
LS0tDQp0aXRsZTogIk1BUkVBTk8gLSBOaU4gLSBoaSBEZW5zIGJlbG93IDE1MDBtIGRlcHRoIg0KYXV0aG9yczogVGhpanMgdmFuIFNvbiwgUnVuZSBIYWx2b3JzZW4sIFJlYmVjY2EgUm9zcywgR2Vub3ZldmEgR29uemFsZXMtTWlyZWxpcywgTWFyZ2FyZXQgRG9sYW4NCmRhdGU6ICJMYXN0IFJlbmRlcmVkIG9uIGByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJUIsICVZJylgIg0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDINCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIGZpZ193aWR0aDogNw0KICAgIGZpZ19oZWlnaHQ6IDcNCmFsd2F5c19hbGxvd19odG1sOiB0cnVlIA0KLS0tDQoNCiMjIFBhY2thZ2VzDQpgYGB7cn0NCmxpYnJhcnkobWFncml0dHIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShnZ2NvcnJwbG90KQ0KbGlicmFyeShnZ3B1YnIpDQpsaWJyYXJ5KHBhdGNod29yaykNCiMgbGlicmFyeSgiaGVyZSIpDQojIGxpYnJhcnkoImJvb2tkb3duIikNCiMgbGlicmFyeSgiZG93bmxvYWR0aGlzIikNCmxpYnJhcnkodmVnYW4pDQpsaWJyYXJ5KHBseXIpDQpsaWJyYXJ5KGUxMDcxKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHZpcmlkaXMpDQpsaWJyYXJ5KEdHYWxseSkNCmxpYnJhcnkoZ2dyZXBlbCkNCmxpYnJhcnkoZ2dvcmRpcGxvdHMpDQpsaWJyYXJ5KHJlYWRyKQ0KbGlicmFyeShSQ29sb3JCcmV3ZXIpDQpsaWJyYXJ5KG9jZSkgDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkocHVycnIpDQpsaWJyYXJ5KGZ1cnJyKQ0KDQoNCnNvdXJjZSgiZ2dfb3JkaXN1cmZfdmlyaWRpcy5SIikNCmBgYA0KDQpMT0FEIE9CSkVDVFMgSUYgSEFWRSBSVU4gQUxSRUFEWQ0KYGBge3J9DQpnZW9kaXN0X3BhPC1yZWFkUkRTKGZpbGUucGF0aChkYXRhUGF0aCwiaW5wdXRzL1NQTElUUy9ERUVQL2hpRGVuc19iMTUwMF9nZW9kaXN0X3BhLnJkcyIpKQ0KZ2VvZGlzdF9yNjwtcmVhZFJEUyhmaWxlLnBhdGgoZGF0YVBhdGgsImlucHV0cy9TUExJVFMvREVFUC9oaURlbnNfYjE1MDBfZ2VvZGlzdF9yNi5yZHMiKSkNCg0KbWRzX3BhPC1yZWFkUkRTKGZpbGUucGF0aChkYXRhUGF0aCwiaW5wdXRzL1NQTElUUy9ERUVQL2hpRGVuc19iMTUwMF9tZHNfcGEucmRzIikpDQptZHNfcjY8LXJlYWRSRFMoZmlsZS5wYXRoKGRhdGFQYXRoLCJpbnB1dHMvU1BMSVRTL0RFRVAvaGlEZW5zX2IxNTAwX21kc19yNi5yZHMiKSkNCiNtZHNfcjZfMTAwMDwtcmVhZFJEUyhmaWxlLnBhdGgoZGF0YVBhdGgsImlucHV0cy9TUExJVFMvREVFUC9oaURlbnNfYjE1MDBfbWRzX3I2XzEwMDByZXAucmRzIikpDQoNCmVwPC0wLjggI2Vwc2lsb24gKHRvIGF2b2lkIGhhdmluZyB0byBmaW5kIGFuZCBydW4ganVzdCB0aGlzIGxpbmUgZnJvbSBjb2RlIGJsb2NrcyB3aGVyZSB0aGUgbWRzIG9iamVjdHMgd2VyZSBtYWRlKQ0KYGBgDQoNCg0KIyMgRGF0YQ0KYGBge3J9DQoNCmVudl9zb3J0IDwtIHJlYWQuY3N2KGZpbGUucGF0aChkYXRhUGF0aCwiaW5wdXRzL1NQTElUUy9ERUVQL0RlZXBCZWxvdzE1MDBfZW52X3NvcnRfMjAyMi0wOS0zMC5jc3YiKSkgJT4lIGFzLmRhdGEuZnJhbWUNCm90dV9zb3J0IDwtIHJlYWQuY3N2KGZpbGUucGF0aChkYXRhUGF0aCwiaW5wdXRzL1NQTElUUy9ERUVQL0RlZXBCZWxvdzE1MDBfb3R1X3NvcnRfMjAyMi0wOS0zMC5jc3YiKSkgJT4lIGFzLmRhdGEuZnJhbWUNCg0KDQp0YWJsZShlbnZfc29ydCRTYW1wSUQyID09IG90dV9zb3J0JFNhbXBJRCkNCg0KDQojbXVzdCBiZSBzYW1lIGxlbmd0aCBhbmQgZXF1YWwgdG8gdW5pcXVlIHNhbXBsZSBudW1iZXIgbGVuZ3RoIC0gdGhlIG9yZGVyZWQgbGlzdHMgYXJlIGFzc3VtZWQgdG8gYmUgZGlyZWN0bHkgcmVsYXRhYmxlIG9uIGEgcm93IGJ5IHJvdyBiYXNpcw0KDQpkaW0oZW52X3NvcnQpDQpkaW0ob3R1X3NvcnQpDQpgYGANCg0KIyMjIyByZW1vdmUgYW55IHZhcmlhYmxlcyB3aXRoIG5vdCBlbm91Z2ggY292ZXJhZ2UNCmBgYHtyfQ0KDQplbnZfc29ydCA8LSBlbnZfc29ydCAlPiUgc2VsZWN0KC1jKCJCTzIyX2xpZ2h0Ym90bWVhbl9iZG1lYW4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJCTzIyX2xpZ2h0Ym90bHRtYXhfYmRtZWFuIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQk8yMl9saWdodGJvdGx0bWluX2JkbWVhbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJPMjJfbGlnaHRib3RyYW5nZV9iZG1lYW4iKSkNCg0KZW52X3NvcnQgPC1lbnZfc29ydCAlPiUgZmlsdGVyKCEoU2FtcElEICVpbiUgYygiMjA1N18wMCIsIjIxODFfMDAiKSkpDQoNCm90dV9zb3J0IDwtb3R1X3NvcnQgJT4lIGZpbHRlcighKFNhbXBJRCAlaW4lIGMoIjIwNTdfMDAiLCIyMTgxXzAwIikpKQ0KYGBgDQoNCg0KIyMjIyBQbG90IG1hcCB0byBjaGVjayBsb2NhdGlvbiBvZiBzYW1wbGVzDQpgYGB7cn0NCmVudl9zb3J0X2xvY2F0aW9uczwtIGdncGxvdChkYXRhID0gZW52X3NvcnQsDQogICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBYLnksDQogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBZKSkgKw0KICB0aGVtZV9jbGFzc2ljKCkgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBiYXRoeSksDQogICAgICAgICAgICAgc2l6ZSA9IDIpICsNCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50Mihsb3cgPSAicmVkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICBtaWQgPSAieWVsbG93IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICBoaWdoID0gImdyZWVuIikgKw0KICBnZ3RpdGxlKCJMb2NhdGlvbiBvZiBzYW1wbGVzIGluIHNvcnRlZCBlbnYgZmlsZSIpDQoNCmVudl9zb3J0X2xvY2F0aW9ucw0KYGBgDQoNCg0KIyMjIERhdGEgY2xlYW5pbmcNCmBgYHtyfQ0KIyMgUmVtb3ZpbmcgTkFzDQpvdHVDb21wbCA8LSBvdHVfc29ydFtjb21wbGV0ZS5jYXNlcyhlbnZfc29ydFssIC1jKDE6d2hpY2goY29sbmFtZXMoZW52X3NvcnQpPT0iYmF0aHkiKS0xKV0pLCBdDQplbnZDb21wbCA8LSBlbnZfc29ydFtjb21wbGV0ZS5jYXNlcyhlbnZfc29ydFssIC1jKDE6d2hpY2goY29sbmFtZXMoZW52X3NvcnQpPT0iYmF0aHkiKS0xKV0pLCBdDQoNCiMjIFJlbW92aW5nIG9ic2VydmF0aW9ucyB3aXRoIGxlc3MgdGhhbiA0IE9UVXMNCnNlbCA8LSByb3dTdW1zKG90dUNvbXBsWywgLWMoMToyKV0pID49IDQNCm90dVNlbCA8LSBvdHVDb21wbFtzZWwsIF0NCmVudlNlbCA8LSBlbnZDb21wbFtzZWwsIF0NCg0KDQojIyBSZW1vdmluZyBwaG9zcGhhdGUNCiNlbnZTZWwgPC0gZW52U2VsICU+JSBzZWxlY3QoLXBob3NwaGF0ZV9tZWFuLnRpZikNCg0KDQpkaW0ob3R1U2VsKTsgZGltKGVudlNlbCkNCmBgYA0KIyMjIyBTaG93IHdoYXQgc2FtcGxlcyBhcmUgbGVmdCBhZnRlciBjb21wbGV0ZSBjYXNlcyBhbmQgPjQgT1RVcyBmaWx0ZXJzDQpgYGB7cn0NCmVudl9zZWxfbG9jYXRpb25zPC0gZ2dwbG90KGRhdGEgPSBlbnZTZWwsDQogICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBYLnksDQogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBZKSkgKw0KICB0aGVtZV9jbGFzc2ljKCkgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBiYXRoeSksDQogICAgICAgICAgICAgc2l6ZSA9IDIpICsNCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50Mihsb3cgPSAicmVkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICBtaWQgPSAieWVsbG93IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICBoaWdoID0gImdyZWVuIikgKw0KICBnZ3RpdGxlKCJMb2NhdGlvbiBvZiBzYW1wbGVzIGluIHNvcnRlZCBlbnYgZmlsZSIpDQoNCmVudl9zZWxfbG9jYXRpb25zDQpgYGANCg0KIyMjIyBTaG93IHdoYXQgZ290IHJlbW92ZWQgaW4gY29tcGxldGUgY2FzZXMgZmlsdGVyDQpgYGB7cn0NCmVudkNvbXBsX2NjcmVtPC1lbnZfc29ydCU+JWZpbHRlcighU2FtcElEJWluJWVudkNvbXBsJFNhbXBJRCkNCg0KZW52Q29tcGxfY2NyZW1fbG9jYXRpb25zPC0gZ2dwbG90KGRhdGEgPSBlbnZDb21wbF9jY3JlbSwNCiAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IFgueSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IFkpKSArDQogIHRoZW1lX2NsYXNzaWMoKSArDQogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IGJhdGh5KSwNCiAgICAgICAgICAgICBzaXplID0gMikgKw0KICBzY2FsZV9jb2xvdXJfZ3JhZGllbnQyKGxvdyA9ICJyZWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIG1pZCA9ICJ5ZWxsb3ciLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGhpZ2ggPSAiZ3JlZW4iKSArDQogIGdndGl0bGUoIkxvY2F0aW9uIG9mIHJlbW92ZWQgY29tcGxldGUgY2FzZSBzYW1wbGVzIHJlc3VsdGluZyBpbiBlbnZTZWwgZmlsZSIpDQoNCmVudkNvbXBsX2NjcmVtX2xvY2F0aW9ucw0KYGBgDQojIyMjIFNob3cgd2hhdCBnb3QgcmVtb3ZlZCBpbiA8NCBPVFVzIGZpbHRlcg0KDQoNCmBgYHtyfQ0KaW52LnNlbCA8LSByb3dTdW1zKG90dUNvbXBsWywgLWMoMToyKV0pIDwgNA0KZW52LmludlNlbCA8LSBlbnZDb21wbFtpbnYuc2VsLCBdDQoNCmVudi5pbnZTZWxfbG9jYXRpb25zPC0gZ2dwbG90KGRhdGEgPSBlbnYuaW52U2VsLA0KICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gWC55LA0KICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gWSkpICsNCiAgdGhlbWVfY2xhc3NpYygpICsNCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gYmF0aHkpLA0KICAgICAgICAgICAgIHNpemUgPSAyKSArDQogIHNjYWxlX2NvbG91cl9ncmFkaWVudDIobG93ID0gInJlZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgbWlkID0gInllbGxvdyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgaGlnaCA9ICJncmVlbiIpICsNCiAgZ2d0aXRsZSgiTG9jYXRpb24gb2YgcmVtb3ZlZCBzYW1wbGVzIHdpdGggPDQgT1RVcyByZXN1bHRpbmcgaW4gZW52U2VsIGZpbGUiKQ0KDQplbnYuaW52U2VsX2xvY2F0aW9ucw0KDQpgYGANCg0KIyMjIFtPcHRpb25hbF0gRGF0YSB0aGlubmluZw0KYGBge3IsIGV2YWw9RkFMU0V9DQojIG90dV9yZWQgPC0gb3R1MVsxOjUwMCwgXQ0KIyBvdHUgPC0gb3R1X3JlZA0KIyANCiMgZW52X3JlZCA8LSBlbnZbMTo1MDAsIF0NCiMgZW52IDwtIGVudl9yZWQNCmBgYA0KDQojIyMgV2hvbGUgZGF0YXNldCBmb3IgZmlyc3QgcnVuDQpgYGB7cn0NCm90dTwtb3R1U2VsWywtYygxOndoaWNoKGNvbG5hbWVzKG90dVNlbCk9PSJBY2VzdGFfZXhjYXZhdGEiKS0xKV0NCmVudjwtZW52U2VsWywgLWMoMjood2hpY2goY29sbmFtZXMoZW52X3NvcnQpPT0iYmF0aHkiKS0xKSldDQoNCnRhYmxlKGlzLm5hKG90dSkpDQoNCnRhYmxlKGlzLm5hKGVudikpDQoNCiNzdHIob3R1U2VsKQ0KI3N0cihlbnZTZWwpDQpgYGANCg0KDQojIyMgU3BsaXR0aW5nIGluIHN1YnNldHMNCmBgYHtyfQ0KIyAjIyBDbGFzcyAxDQojIG90dTEgPC0gc3Vic2V0KG90dVNlbCwgZW52U2VsJFNwbGl0UmV2ID09IDEpDQojIGVudjEgPC0gZW52U2VsICU+JSBmaWx0ZXIoU3BsaXRSZXYgPT0gMSkNCiMgDQojICMjIENsYXNzIDINCiMgb3R1MiA8LSBzdWJzZXQob3R1U2VsLCBlbnZTZWwkU3BsaXRSZXYgPT0gMikNCiMgZW52MiA8LSBlbnZTZWwgJT4lIGZpbHRlcihTcGxpdFJldiA9PSAyKQ0KIyANCiMgIyMgQ2xhc3MgNA0KIyBvdHUzIDwtIHN1YnNldChvdHVTZWwsIGVudlNlbCRTcGxpdFJldiA9PSAzKQ0KIyBlbnYzIDwtIGVudlNlbCAlPiUgZmlsdGVyKFNwbGl0UmV2ID09IDMpDQojIA0KIyAjIyBDbGFzcyA0DQojIG90dTQgPC0gc3Vic2V0KG90dVNlbCwgZW52U2VsJFNwbGl0UmV2ID09IDQpDQojIGVudjQgPC0gZW52U2VsICU+JSBmaWx0ZXIoU3BsaXRSZXYgPT0gNCkNCiMgDQojICMjIENsYXNzIDYNCiMgb3R1NiA8LSBzdWJzZXQob3R1U2VsLCBlbnZTZWwkU3BsaXRSZXYgPT0gNikNCiMgZW52NiA8LSBlbnZTZWwgJT4lIGZpbHRlcihTcGxpdFJldiA9PSA2KQ0KIyANCiMgIyMgQ2xhc3MgNw0KIyBvdHU3IDwtIHN1YnNldChvdHVTZWwsIGVudlNlbCRTcGxpdFJldiA9PSA3KQ0KIyBlbnY3IDwtIGVudlNlbCAlPiUgZmlsdGVyKFNwbGl0UmV2ID09IDcpDQojIA0KIyAjIyBDbGFzcyA4DQojIG90dTggPC0gc3Vic2V0KG90dVNlbCwgZW52U2VsJFNwbGl0UmV2ID09IDgpDQojIGVudjggPC0gZW52U2VsICU+JSBmaWx0ZXIoU3BsaXRSZXYgPT0gOCkNCmBgYA0KDQojIyBTZWxlY3Rpbmcgc3Vic2V0DQpgYGB7cn0NCiMgIyMgU2VsZWN0aW5nIA0KIyBvdHUgPC0gb3R1Mg0KIyBlbnYgPC0gZW52Mg0KDQpgYGANCg0KIyMgZm9ybWF0IGRhdGEgY29ycmVjdGx5DQpgYGB7cn0NCiNlbnYkY29vcmRzLngxPC1hcy5udW1lcmljKGVudiRjb29yZHMueDEpDQojZW52JGNvb3Jkcy54MjwtYXMubnVtZXJpYyhlbnYkY29vcmRzLngyKQ0KDQoNCmBgYA0KDQoNCiMjIEFidW5kYW5jZSB3ZWlnaHRpbmcNCk1ha2UgZnVuY3Rpb24NCllvdSBtaWdodCBoYXZlIHRvIGRyb3AgdmFyaWFibGVzIHRoYXQgaGF2ZSBiZWVuIGltcG9ydGVkIGFzIGNoYXJhY3Rlcg0KYGBge3J9DQoNCm90dV9wYSA8LSBkZWNvc3RhbmQoeCA9IG90dVssIC1jKDEpXSwNCiAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gInBhIikNCg0KIyB5ID0gYXhedyAgICAgICAgICMgcG93ZXIgdHJhbnNmb3JtYXRpb24gZm9ybXVsYQ0KZHQgPC0gb3R1WywgLWMoMToyKV0gICAgICAgICAgIyBzcGVjaWVzIGRhdGEgdG8gdHJhbnNmb3JtDQp4X21uIDwtIG1pbihkdFtkdCA+IDBdKQ0KeF9teCA8LSBtYXgoZHQpDQpybmcgPC0gNiAgICAgICAgICAgIyBhYnVuZGFuY2UgcmFuZ2UNCncgPC0gbG9nKHJuZykgLyAobG9nKHhfbXgpIC0gbG9nKHhfbW4pKQ0KYSA8LSB4X21uXigtdykNCiMgb3R1XzYgPC0gYSAqIGR0WywgLWMoMTozKV1edw0Kb3R1XzYgPC0gYSAqIGR0XncNCnJhbmdlKG90dV82KQ0KYGBgDQoNCiMjIE9yZGluYXRpb24gbWV0aG9kcw0KIyMjIERDQSBQQQ0KYGBge3J9DQpTeXMudGltZSgpDQpkY2FfcGEgPC0gZGVjb3JhbmEodmVnID0gb3R1X3BhKQ0KDQpwcmludChkY2FfcGEsIGhlYWQ9VCkNCmBgYA0KDQoNCiMjIyBHTk1EUyBQQQ0KDQojIyMjIERpc3RhbmNlcyAtIGRvbid0IHJ1biBpZiBsb2FkaW5nIHNhdmVkIG9iamVjdA0KDQpgYGB7cn0NCiMjIEJyYXktQ3VydGlzDQpkaXN0X3BhIDwtIHZlZ2Rpc3QoeCA9IG90dV9wYSwgbWV0aG9kID0gImJyYXkiKQ0KDQojIyBHZW9kaXN0DQplcCA8LSAwLjggICAgICMgZXBzaWxvbg0KZ2VvZGlzdF9wYSA8LSBpc29tYXBkaXN0KGRpc3QgPSBkaXN0X3BhLCBlcHNpbG9uID0gZXApDQpgYGANCg0KDQojIyMjIyBTYXZlIHRoZSByZXN1bHQgLSBkb24ndCBydW4gaWYgbG9hZGluZyBzYXZlZCBvYmplY3QNCmBgYHtyfQ0Kc2F2ZVJEUyhnZW9kaXN0X3BhLA0KICAgICAgICBmaWxlID0gKGZpbGUucGF0aChkYXRhUGF0aCwiaW5wdXRzL1NQTElUUy9ERUVQL2hpRGVuc19iMTUwMF9nZW9kaXN0X3BhLnJkcyIpKSkNCmBgYA0KDQojIyMjIE9yZGluYXRpb24NCg0KIyMjIyBtb25vTURTIC0gZG9uJ3QgcnVuIGlmIGxvYWRpbmcgc2F2ZWQgb2JqZWN0DQp0b29rIDYuNG1pbnMgd2l0aCBqb25hdGFuJ3MgcGFyYWxlbGwgc29sdXRpb24gKHVzdWFsbHkgfjEwbWlucykNCg0KVXNpbmcgb2xkIHZlcnNpb24gd2l0aG91dCBmdXJycg0KYGBge3J9DQoNCiBTeXMudGltZSgpDQogZCA8LSAyDQogbWRzX3BhIDwtIGxpc3QoKQ0KDQogZm9yIChpIGluIDE6MTAwKSB7DQogICBtZHNfcGFbW2ldXTwtbW9ub01EUyhnZW9kaXN0X3BhLA0KICAgICAgICAgICAgICAgICAgICAgbWF0cml4KGMocnVuaWYoZGltKG90dV9wYSlbMV0qZCkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5yb3cgPSBkaW0ob3R1X3BhKVsxXSksDQogICAgICAgICAgICAgICAgICAgICBrID0gZCwNCiAgICAgICAgICAgICAgICAgICAgIG1vZGVsID0gImdsb2JhbCIsDQogICAgICAgICAgICAgICAgICAgICBtYXhpdCA9IDIwMDAsDQogICAgICAgICAgICAgICAgICAgICBzbWluID0gMWUtNywNCiAgICAgICAgICAgICAgICAgICAgIHNmZ3JtaW4gPSAxZS03KQ0KIH0NCiBTeXMudGltZSgpDQoNCg0KIyAjIG1vbm9NRFMNCiMgIGQgPC0gMg0KIyBtZHNfcjYgPC0gbGlzdCgpDQojIA0KIyAgU3lzLnRpbWUoKQ0KIyAgZm9yIChpIGluIDE6MjAwKSB7DQojICAgIG1kc19yNltbaV1dPC1tb25vTURTKGdlb2Rpc3RfcjYsDQojICAgICAgICAgICAgICAgICAgICAgIG1hdHJpeChjKHJ1bmlmKGRpbShvdHVfNilbMV0qZCkpLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnJvdyA9IGRpbShvdHVfNilbMV0pLA0KIyAgICAgICAgICAgICAgICAgICAgICBrID0gZCwNCiMgICAgICAgICAgICAgICAgICAgICAgbW9kZWwgPSAiZ2xvYmFsIiwNCiMgICAgICAgICAgICAgICAgICAgICAgbWF4aXQgPSAyMDAwLA0KIyAgICAgICAgICAgICAgICAgICAgICBzbWluID0gMWUtNywNCiMgICAgICAgICAgICAgICAgICAgICAgc2Zncm1pbiA9IDFlLTcpIyB9DQojICBTeXMudGltZSgpDQoNCiMgI0pvbmF0YW4ncyB1cGdyYWRlIGZvciBzcGVlZCAtIHBhcmFsbGVsIHByb2Nlc3Npbmcgb2YgbWRzIHJlcHRpdGlvbnMNCiMgbGlicmFyeShmdXJycikgIyBQYWNrYWdlIHRvIHJ1biBub24gc2VxdWVudGlhbCBmdW5jdGlvbnMgaW4gcGFyYWxsZWwNCiMgI2xpYnJhcnkocHVycnIpDQojIHBsYW4obXVsdGlzZXNzaW9uKQ0KIyANCiMgZCA8LSAyDQojIA0KIyBpIDwtMjAwICMgbnVtYmVyIG9mIHJlcHMNCiMgDQojIExpc3RfZ2VvZGlzdF9wYSA8LSBsYXBwbHkoc2VxX2xlbihpKSwgZnVuY3Rpb24oWCkgZ2VvZGlzdF9wYSkgIyBtYWtlcyBhIGxpc3Qgd2l0aCB0aGUgaW5wdXQgZGF0YSByZXBlYXRlZCBhcyBtYW55IHRpbWVzIGFzIHJlcHMgd2FudGVkDQojIHN0YXJ0X3Q8LVN5cy50aW1lKCkNCiMgWG1kc19wYTwtZnVycnI6OmZ1dHVyZV9tYXAoTGlzdF9nZW9kaXN0X3BhLCANCiMgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgbW9ub01EUyh4LA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hdHJpeChjKHJ1bmlmKGRpbShvdHVfNilbMV0qZCkpLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBucm93ID0gZGltKG90dV82KVsxXSksDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgayA9IGQsDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWwgPSAiZ2xvYmFsIiwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXhpdCA9IDIwMDAsDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc21pbiA9IDFlLTcsDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Zncm1pbiA9IDFlLTcpLA0KIyAgICAgICAgICAgICAgICAgICAucHJvZ3Jlc3MgPSBUUlVFKQ0KIyBTeXMudGltZSgpIC0gc3RhcnRfdA0KDQoNCg0KYGBgDQoNCiMjIyMjIFNhdmUgdGhlIHJlc3VsdCAtIGRvbid0IHJ1biBpZiBsb2FkaW5nIHNhdmVkIG9iamVjdA0KY2FuIHRha2UgYSBmZXcgbWlucw0KYGBge3J9DQpzYXZlUkRTKG1kc19wYSwNCiAgICAgICAgZmlsZSA9IGZpbGUucGF0aChkYXRhUGF0aCwiaW5wdXRzL1NQTElUUy9ERUVQL2hpRGVuc19iMTUwMF9tZHNfcGEucmRzIikpDQpgYGANCg0KDQojIyMjIyBCZXN0IG5tZHMgc29sdXRpb24gLSBQQQ0KTWFrZSBmdW5jdGlvbj8NCmBgYHtyLCBldmFsPVRSVUV9DQojIExvYWRpbmcgZ2VvZGlzdCBvYmplY3QNCiMgZ2VvZGlzdF9uZmkgPC0gcmVhZFJEUyhmaWxlID0gIi4uL1NwbGl0UmV2Ml9nZW9kaXN0X3BhX2Z1bGwucmRzIikNCmBgYA0KDQoNCmBgYHtyfQ0KIyBMb2FkaW5nIG1kcyByZXN1bHRzDQojIG1kcyA8LSByZWFkUkRTKGZpbGUgPSAiLi4vU3BsaXRSZXYyX21kc19wYV9mdWxsLnJkcyIpDQoNCiMjIEV4dHJhY3RpbmcgdGhlIHN0cmVzcyBvZiBlYWNoIG5tZHMgaXRlcmF0aW9uDQptZHNfc3RyZXNzX3BhPC11bmxpc3QobGFwcGx5KG1kc19wYSwgZnVuY3Rpb24odil7dltbMjJdXX0pKSANCg0Kb3JkZXJlZF9wYSA8LW9yZGVyKG1kc19zdHJlc3NfcGEpDQoNCiMjIEJlc3QsIHNlY29uZCBiZXN0LCBhbmQgd29yc3Qgc29sdXRpb24NCm1kc19zdHJlc3NfcGFbb3JkZXJlZF9wYVsxXV0NCm1kc19zdHJlc3NfcGFbb3JkZXJlZF9wYVsyXV0NCm1kc19zdHJlc3NfcGFbb3JkZXJlZF9wYVsxMF1dDQoNCiMjIFNjYWxpbmcgb2YgYXhlcyB0byBoYWxmIGNoYW5nZSB1bml0cyBhbmQgdmFyaW1heCByb3RhdGlvbiBieSBwb3N0TURTDQptZHNfYmVzdF9wYTwtcG9zdE1EUyhtZHNfcGFbW29yZGVyZWRfcGFbMV1dXSwNCiAgICAgICAgICAgICAgICAgIGdlb2Rpc3RfcGEsIA0KICAgICAgICAgICAgICAgICAgcGMgPSBUUlVFLCANCiAgICAgICAgICAgICAgICAgIGhhbGZjaGFuZ2UgPSBUUlVFLCANCiAgICAgICAgICAgICAgICAgIHRocmVzaG9sZCA9IGVwKSAgICAgIyBJcyB0aGlzIHRocmVzaG9sZCByZWxhdGVkIHRvIHRoZSBlcHNpbG9uIGFib3ZlPw0KbWRzX2Jlc3RfcGENCg0KbWRzX3NlY2Jlc3RfcGEgPC0gcG9zdE1EUyhtZHNfcGFbW29yZGVyZWRfcGFbMl1dXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VvZGlzdF9wYSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHBjID0gVFJVRSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGhhbGZjaGFuZ2UgPSBUUlVFLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgdGhyZXNob2xkID0gZXApDQptZHNfc2VjYmVzdF9wYQ0KDQojIyBQcm9jcnVzdGVzIGNvbXBhcmlzb25zDQpwcm9jcl9wYSA8LSBwcm9jcnVzdGVzKG1kc19iZXN0X3BhLA0KICAgICAgICAgICAgICAgICAgICBtZHNfc2VjYmVzdF9wYSwNCiAgICAgICAgICAgICAgICAgICAgcGVybXV0YXRpb25zPTk5OSkNCnByb3Rlc3QobWRzX2Jlc3RfcGEsDQogICAgICAgIG1kc19zZWNiZXN0X3BhLA0KICAgICAgICBwZXJtdXRhdGlvbnM9OTk5KQ0KDQpwbG90KHByb2NyX3BhKQ0KDQpwbmcoZmlsZT1maWxlLnBhdGgoZGF0YVBhdGgsIm91dHB1dHMvaGlEZW5zX2IxNTAwX3Byb2NydXN0ZXNfcGEucG5nIiksIHdpZHRoPTEwMDAsIGhlaWdodD03MDApDQpwbG90KHByb2NyX3BhKQ0KZGV2Lm9mZigpDQpgYGANCg0KIyMjIyMgQ29ycmVsYXRpb24gb2YgYXhpczogRENBIHZzIE5NRFMgLSBQQQ0KYGBge3J9DQojIEV4dHJhY3Rpbmcgb3JkaW5hdGlvbiBheGlzDQpheCA8LSAyDQpheGlzX3BhIDwtIGNiaW5kKG1kc19iZXN0X3BhJHBvaW50cywNCiAgICAgICAgICAgICAgICAgc2NvcmVzKGRjYV9wYSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGRpc3BsYXkgPSAic2l0ZXMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgb3JpZ2luID0gVFJVRSlbLCAxOmF4XSkNCg0KZ2djb3JyKGF4aXNfcGEsIA0KICAgICAgIG1ldGhvZD1jKCJldmVyeXRoaW5nIiwia2VuZGFsbCIpLCANCiAgICAgICBsYWJlbCA9IFRSVUUsDQogICAgICAgbGFiZWxfc2l6ZSA9IDMsIA0KICAgICAgIGxhYmVsX2NvbG9yID0gImJsYWNrIiwgIA0KICAgICAgIG5icmVha3MgPSA4LA0KICAgICAgIGxhYmVsX3JvdW5kID0gMywNCiAgICAgICBsb3cgPSAicmVkIiwNCiAgICAgICBtaWQgPSAid2hpdGUiLA0KICAgICAgIGhpZ2ggPSAiZ3JlZW4iKQ0KDQoNCmBgYA0KDQojIyMjIyBTYXZlIHRoZSBmaWd1cmUNCmBgYHtyfQ0KZ2dzYXZlKGZpbGVuYW1lID0gZmlsZS5wYXRoKGRhdGFQYXRoLCJvdXRwdXRzL2hpRGVuc19iMTUwMF9jb3JyZWxhdGlvblBDQXZzTk1EU19QQS5wbmciKSwNCiAgICAgICBkZXZpY2UgPSAicG5nIiwNCiAgICAgICBkcGk9MzAwICkNCg0KIyBTd2l0Y2hpbmcgZGlyZWN0aW9uIG9mIE5NRFMxDQojIG1kc19iZXN0JHBvaW50c1ssIDFdIDwtIC1tZHNfYmVzdCRwb2ludHNbLCAxXQ0KYGBgDQoNCiMjIyBEQ0EgUjYNCmBgYHtyfQ0KZGNhX3I2IDwtIGRlY29yYW5hKHZlZyA9IG90dV82KQ0KDQpwcmludChkY2FfcjYsIGhlYWQ9VCkNCmBgYA0KDQoNCiMjIyBHTk1EUyBSNg0KIyMjIyBEaXN0YW5jZXMgLSBkb24ndCBydW4gaWYgbG9hZGluZyBzYXZlZCBvYmplY3QNCmBgYHtyfQ0KIyMgQnJheS1DdXJ0aXMNCmRpc3RfcjYgPC0gdmVnZGlzdCh4ID0gb3R1XzYsIG1ldGhvZCA9ICJicmF5IikNCg0KIyMgR2VvZGlzdA0KZXAgPC0gMC44MCAgICAgIyBlcHNpbG9uDQpnZW9kaXN0X3I2IDwtIGlzb21hcGRpc3QoZGlzdCA9IGRpc3RfcjYsIGVwc2lsb24gPSBlcCkNCmBgYA0KDQojIyMjIyBTYXZlIHRoZSByZXN1bHQgLSBkb24ndCBydW4gaWYgbG9hZGluZyBzYXZlZCBvYmplY3QNCmBgYHtyfQ0Kc2F2ZVJEUyhnZW9kaXN0X3I2LA0KICAgICAgICBmaWxlID0gKGZpbGUucGF0aChkYXRhUGF0aCwiaW5wdXRzL1NQTElUUy9ERUVQL2hpRGVuc19iMTUwMF9nZW9kaXN0X3I2LnJkcyIpKSkNCmBgYA0KDQoNCiMjIyMgT3JkaW5hdGlvbiANCg0KIyMjIyMgMjAwIHJlcHMgLSBkb24ndCBydW4gaWYgbG9hZGluZyBzYXZlZCBvYmplY3QNCihKb25hdGFuJ3MgcGFyYWxsZWwgc29sdXRpb24gdG9vayA1LjRtaW5zLCBiZWZvcmUgaXQgdG9vayAxMG1pbnMpDQoNClVzaW5nIG9sZGVyIHZlcnNpb24gd2l0aG91dCBmdXJyci4NCmBgYHtyfQ0KIyMgbW9ub01EUw0KZCA8LSAyDQptZHNfcjYgPC0gbGlzdCgpDQoNClN5cy50aW1lKCkNCmZvciAoaSBpbiAxOjIwMCkgew0KICBtZHNfcjZbW2ldXTwtbW9ub01EUyhnZW9kaXN0X3I2LA0KICAgICAgICAgICAgICAgICAgICBtYXRyaXgoYyhydW5pZihkaW0ob3R1XzYpWzFdKmQpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG5yb3cgPSBkaW0ob3R1XzYpWzFdKSwNCiAgICAgICAgICAgICAgICAgICAgayA9IGQsDQogICAgICAgICAgICAgICAgICAgIG1vZGVsID0gImdsb2JhbCIsDQogICAgICAgICAgICAgICAgICAgIG1heGl0ID0gMjAwMCwNCiAgICAgICAgICAgICAgICAgICAgc21pbiA9IDFlLTcsDQogICAgICAgICAgICAgICAgICAgIHNmZ3JtaW4gPSAxZS03KQ0KfQ0KU3lzLnRpbWUoKQ0KDQojICNKb25hdGFuJ3MgdXBncmFkZSBmb3Igc3BlZWQgLSBwYXJhbGxlbCBwcm9jZXNzaW5nIG9mIG1kcyByZXB0aXRpb25zDQojICNsaWJyYXJ5KGZ1cnJyKSAjIFBhY2thZ2UgdG8gcnVuIG5vbiBzZXF1ZW50aWFsIGZ1bmN0aW9ucyBpbiBwYXJhbGxlbA0KIyAjbGlicmFyeShwdXJycikNCiMgcGxhbihtdWx0aXNlc3Npb24pDQojIA0KIyBkIDwtIDINCiMgDQojIGkgPC0yMDAgIyBudW1iZXIgb2YgcmVwcw0KIyANCiMgTGlzdF9nZW9kaXN0X3I2IDwtIGxhcHBseShzZXFfbGVuKGkpLCBmdW5jdGlvbihYKSBnZW9kaXN0X3I2KSAjIG1ha2VzIGEgbGlzdCB3aXRoIHRoZSBpbnB1dCBkYXRhIHJlcGVhdGVkIGFzIG1hbnkgdGltZXMgYXMgcmVwcyB3YW50ZWQNCiMgc3RhcnRfdDwtU3lzLnRpbWUoKQ0KIyBYbWRzX3I2PC1mdXJycjo6ZnV0dXJlX21hcChMaXN0X2dlb2Rpc3RfcjYsIA0KIyAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSBtb25vTURTKHgsDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF0cml4KGMocnVuaWYoZGltKG90dV82KVsxXSpkKSksDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5yb3cgPSBkaW0ob3R1XzYpWzFdKSwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrID0gZCwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlbCA9ICJnbG9iYWwiLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heGl0ID0gMjAwMCwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzbWluID0gMWUtNywNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZmdybWluID0gMWUtNyksDQojICAgICAgICAgICAgICAgICAgIC5wcm9ncmVzcyA9IFRSVUUpDQojIFN5cy50aW1lKCkgLSBzdGFydF90DQoNCmBgYA0KDQojIyMjIyBTYXZlIHRoZSByZXN1bHQgLSBkb24ndCBydW4gaWYgbG9hZGluZyBzYXZlZCBvYmplY3QNCmBgYHtyfQ0Kc2F2ZVJEUyhtZHNfcjYsDQogICAgICAgIGZpbGUgPSBmaWxlLnBhdGgoZGF0YVBhdGgsImlucHV0cy9TUExJVFMvREVFUC9oaURlbnNfYjE1MDBfbWRzX3I2LnJkcyIpKSANCmBgYA0KDQojIyMjIyBCZXN0IG5tZHMgc29sdXRpb24gcjYgMjAwIHJlcCANCmBgYHtyLCBldmFsPVRSVUV9DQojIExvYWRpbmcgZ2VvZGlzdCBvYmplY3QNCiMgZ2VvZGlzdF9uZmkgPC0gcmVhZFJEUyhmaWxlID0gIi4uL1NwbGl0UmV2Ml9nZW9kaXN0X25maS5yZHMiKQ0KDQojIExvYWRpbmcgbWRzIHJlc3VsdHMNCiMgbWRzIDwtIHJlYWRSRFMoZmlsZSA9ICIuLi9TcGxpdFJldjJfbWRzLnJkcyIpDQoNCiMjIEV4dHJhY3RpbmcgdGhlIHN0cmVzcyBvZiBlYWNoIG5tZHMgaXRlcmF0aW9uDQptZHNfc3RyZXNzX3I2PC11bmxpc3QobGFwcGx5KG1kc19yNiwgZnVuY3Rpb24odil7dltbMjJdXX0pKSANCg0Kb3JkZXJlZF9yNiA8LW9yZGVyKG1kc19zdHJlc3NfcjYpDQoNCiMjIEJlc3QsIHNlY29uZCBiZXN0LCBhbmQgd29yc3Qgc29sdXRpb24NCm1kc19zdHJlc3NfcjZbb3JkZXJlZF9yNlsxXV0NCm1kc19zdHJlc3NfcjZbb3JkZXJlZF9yNlsyXV0NCm1kc19zdHJlc3NfcjZbb3JkZXJlZF9yNlsxMDBdXQ0KDQojIyBTY2FsaW5nIG9mIGF4ZXMgdG8gaGFsZiBjaGFuZ2UgdW5pdHMgYW5kIHZhcmltYXggcm90YXRpb24gYnkgcG9zdE1EUw0KbWRzX2Jlc3RfcjY8LXBvc3RNRFMobWRzX3I2W1tvcmRlcmVkX3I2WzFdXV0sDQogICAgICAgICAgICAgICAgICAgICBnZW9kaXN0X3I2LCANCiAgICAgICAgICAgICAgICAgICAgIHBjID0gVFJVRSwgDQogICAgICAgICAgICAgICAgICAgICBoYWxmY2hhbmdlID0gVFJVRSwgDQogICAgICAgICAgICAgICAgICAgICB0aHJlc2hvbGQgPSBlcCkgICAgICMgSXMgdGhpcyB0aHJlc2hvbGQgcmVsYXRlZCB0byB0aGUgZXBzaWxvbiBhYm92ZT8NCm1kc19iZXN0X3I2DQoNCm1kc19zZWNiZXN0X3I2PC1wb3N0TURTKG1kc19yNltbb3JkZXJlZF9yNlsyXV1dLA0KICAgICAgICAgICAgICAgICAgICAgICAgZ2VvZGlzdF9yNiwgDQogICAgICAgICAgICAgICAgICAgICAgICBwYyA9IFRSVUUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgaGFsZmNoYW5nZSA9IFRSVUUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgdGhyZXNob2xkID0gZXApDQptZHNfc2VjYmVzdF9yNg0KDQojIyBQcm9jcnVzdGVzIGNvbXBhcmlzb25zDQpwcm9jcl9yNiA8LSBwcm9jcnVzdGVzKG1kc19iZXN0X3I2LA0KICAgICAgICAgICAgICAgICAgICAgICBtZHNfc2VjYmVzdF9yNiwNCiAgICAgICAgICAgICAgICAgICAgICAgcGVybXV0YXRpb25zPTk5OSkNCnByb3Rlc3QobWRzX2Jlc3RfcjYsDQogICAgICAgIG1kc19zZWNiZXN0X3I2LA0KICAgICAgICBwZXJtdXRhdGlvbnM9OTk5KQ0KDQpwbG90KHByb2NyX3I2KQ0KDQpwbmcoZmlsZS5wYXRoKGRhdGFQYXRoLCJvdXRwdXRzL2hpRGVuc19iMTUwMF9wcm9jcnVzdGVzX3I2LnBuZyIpLCB3aWR0aD0xMDAwLCBoZWlnaHQ9NzAwLCkgI2FkZGVkIDEwMDANCnBsb3QocHJvY3JfcjYpDQpkZXYub2ZmKCkNCmBgYA0KDQogIyMjIyAxMDAwIHJlcHMgLSBkb24ndCBydW4gaWYgbG9hZGluZyBzYXZlZCBvYmplY3QgDQogQ3VycmVudGx5IGNvbW1lbnRlZCBvdXQgYXMgaXQgZ2FpbmVkIG5vdGhpbmcgYnV0IHRvb2sgZXh0cmEgdGltZS4gQ2FuIGJlIHJlbW92ZWQgaW4gZHVlIGNvdXJzZS4NCjwhLS0gdG9vayAxaHIgLS0+DQo8IS0tIGBgYHtyfSAtLT4NCjwhLS0gIyBtb25vTURTIC0tPg0KPCEtLSBkIDwtIDIgLS0+DQo8IS0tIG1kc19yNl8xMDAwIDwtIGxpc3QoKSNvYmogYWRkZWRfMTAwMCAtLT4NCg0KPCEtLSBTeXMudGltZSgpIC0tPg0KPCEtLSBmb3IgKGkgaW4gMToxMDAwKSB7I2NoYW5nZSBmcm9tIDIwMCAoMjAwIHJhIGluIDIwbWluLCAxMDAwIHJhbiBpbiAxaHIpIC0tPg0KPCEtLSAgIG1kc19yNl8xMDAwW1tpXV08LW1vbm9NRFMoZ2VvZGlzdF9yNiwjb2JqIGFkZGVkIF8xMDAwIC0tPg0KPCEtLSAgICAgICAgICAgICAgICAgICAgIG1hdHJpeChjKHJ1bmlmKGRpbShvdHVfNilbMV0qZCkpLCAtLT4NCjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgICAgbnJvdyA9IGRpbShvdHVfNilbMV0pLCAtLT4NCjwhLS0gICAgICAgICAgICAgICAgICAgICBrID0gZCwgLS0+DQo8IS0tICAgICAgICAgICAgICAgICAgICAgbW9kZWwgPSAiZ2xvYmFsIiwgLS0+DQo8IS0tICAgICAgICAgICAgICAgICAgICAgbWF4aXQgPSAyMDAwLCAtLT4NCjwhLS0gICAgICAgICAgICAgICAgICAgICBzbWluID0gMWUtNywgLS0+DQo8IS0tICAgICAgICAgICAgICAgICAgICAgc2Zncm1pbiA9IDFlLTcpIC0tPg0KPCEtLSB9IC0tPg0KPCEtLSBTeXMudGltZSgpIC0tPg0KPCEtLSBgYGAgLS0+DQoNCjwhLS0gIyMjIyMgU2F2ZSB0aGUgcmVzdWx0IC0gZG9uJ3QgcnVuIGlmIGxvYWRpbmcgc2F2ZWQgb2JqZWN0IC0tPg0KPCEtLSBgYGB7cn0gLS0+DQo8IS0tIHNhdmVSRFMobWRzX3I2XzEwMDAsICNvYmogYWRkZWRfMTAwMCAtLT4NCjwhLS0gICAgICAgICBmaWxlID0gZmlsZS5wYXRoKGRhdGFQYXRoLCJpbnB1dHMvU1BMSVRTL0RFRVAvbWRzX3I2XzEwMDByZXAucmRzIikpICNjaGFuZ2UgZnJvbSAyMDAgLS0+DQo8IS0tIGBgYCAtLT4NCg0KPCEtLSAjIyMjIyBCZXN0IG5tZHMgc29sdXRpb24gcjYgMTAwMCByZXAgLS0+DQo8IS0tIGBgYHtyLCBldmFsPVRSVUV9IC0tPg0KPCEtLSAjIExvYWRpbmcgZ2VvZGlzdCBvYmplY3QgLS0+DQo8IS0tICMgZ2VvZGlzdF9uZmkgPC0gcmVhZFJEUyhmaWxlID0gIi4uL1NwbGl0UmV2Ml9nZW9kaXN0X25maS5yZHMiKSAtLT4NCg0KPCEtLSAjIExvYWRpbmcgbWRzIHJlc3VsdHMgLS0+DQo8IS0tICMgbWRzIDwtIHJlYWRSRFMoZmlsZSA9ICIuLi9TcGxpdFJldjJfbWRzLnJkcyIpIC0tPg0KDQo8IS0tICMjIEV4dHJhY3RpbmcgdGhlIHN0cmVzcyBvZiBlYWNoIG5tZHMgaXRlcmF0aW9uIC0tPg0KPCEtLSBtZHNfc3RyZXNzX3I2XzEwMDA8LXVubGlzdChsYXBwbHkobWRzX3I2XzEwMDAsIGZ1bmN0aW9uKHYpe3ZbWzIyXV19KSkgI2FkZWVkIDEwMDAgLS0+DQoNCjwhLS0gb3JkZXJlZF9yNl8xMDAwIDwtb3JkZXIobWRzX3N0cmVzc19yNl8xMDAwKSAtLT4NCg0KPCEtLSAjIyBCZXN0LCBzZWNvbmQgYmVzdCwgYW5kIHdvcnN0IHNvbHV0aW9uIC0tPg0KPCEtLSBtZHNfc3RyZXNzX3I2XzEwMDBbb3JkZXJlZF9yNl8xMDAwWzFdXSAtLT4NCjwhLS0gbWRzX3N0cmVzc19yNl8xMDAwW29yZGVyZWRfcjZfMTAwMFsyXV0gLS0+DQo8IS0tIG1kc19zdHJlc3NfcjZfMTAwMFtvcmRlcmVkX3I2XzEwMDBbMTAwXV0gLS0+DQoNCjwhLS0gIyMgU2NhbGluZyBvZiBheGVzIHRvIGhhbGYgY2hhbmdlIHVuaXRzIGFuZCB2YXJpbWF4IHJvdGF0aW9uIGJ5IHBvc3RNRFMgLS0+DQo8IS0tIG1kc19iZXN0X3I2XzEwMDA8LXBvc3RNRFMobWRzX3I2XzEwMDBbW29yZGVyZWRfcjZfMTAwMFsxXV1dLCAtLT4NCjwhLS0gICAgICAgICAgICAgICAgICAgICAgZ2VvZGlzdF9yNiwgIC0tPg0KPCEtLSAgICAgICAgICAgICAgICAgICAgICBwYyA9IFRSVUUsICAtLT4NCjwhLS0gICAgICAgICAgICAgICAgICAgICAgaGFsZmNoYW5nZSA9IFRSVUUsICAtLT4NCjwhLS0gICAgICAgICAgICAgICAgICAgICAgdGhyZXNob2xkID0gZXApICAgICAjIElzIHRoaXMgdGhyZXNob2xkIHJlbGF0ZWQgdG8gdGhlIGVwc2lsb24gYWJvdmU/IC0tPg0KPCEtLSBtZHNfYmVzdF9yNl8xMDAwIC0tPg0KDQo8IS0tIG1kc19zZWNiZXN0X3I2XzEwMDA8LXBvc3RNRFMobWRzX3I2XzEwMDBbW29yZGVyZWRfcjZfMTAwMFsyXV1dLCAtLT4NCjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgZ2VvZGlzdF9yNiwgIC0tPg0KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICBwYyA9IFRSVUUsICAtLT4NCjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgaGFsZmNoYW5nZSA9IFRSVUUsICAtLT4NCjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgdGhyZXNob2xkID0gZXApIC0tPg0KPCEtLSBtZHNfc2VjYmVzdF9yNl8xMDAwIC0tPg0KDQo8IS0tICMjIFByb2NydXN0ZXMgY29tcGFyaXNvbnMgLS0+DQo8IS0tIHByb2NyX3I2XzEwMDAgPC0gcHJvY3J1c3RlcyhtZHNfYmVzdF9yNl8xMDAwLCAtLT4NCjwhLS0gICAgICAgICAgICAgICAgICAgICAgICBtZHNfc2VjYmVzdF9yNl8xMDAwLCAtLT4NCjwhLS0gICAgICAgICAgICAgICAgICAgICAgICBwZXJtdXRhdGlvbnM9OTk5KSAtLT4NCjwhLS0gcHJvdGVzdChtZHNfYmVzdF9yNl8xMDAwLCAtLT4NCjwhLS0gICAgICAgICBtZHNfc2VjYmVzdF9yNl8xMDAwLCAtLT4NCjwhLS0gICAgICAgICBwZXJtdXRhdGlvbnM9OTk5KSAtLT4NCg0KPCEtLSBwbG90KHByb2NyX3I2XzEwMDApIC0tPg0KDQo8IS0tIHBuZyhmaWxlLnBhdGgoZGF0YVBhdGgsIm91dHB1dHMvcHJvY3J1c3Rlc19yNl8xMDAwLnBuZyIpLCB3aWR0aD0xMDAwLCBoZWlnaHQ9NzAwLCkgI2FkZGVkIDEwMDAgLS0+DQo8IS0tIHBsb3QocHJvY3JfcjZfMTAwMCkgLS0+DQo8IS0tIGRldi5vZmYoKSAtLT4NCjwhLS0gYGBgIC0tPg0KDQoNCg0KIyMjIyMgQ29ycmVsYXRpb24gb2YgYXhpczogRENBIHZzIE5NRFMgDQpyZXRhaW4gdGhlIDIwMCByZXAgdmVyc2lvbg0KYGBge3J9DQojIEV4dHJhY3Rpbmcgb3JkaW5hdGlvbiBheGlzDQpheCA8LSAyDQpheGlzX3I2IDwtIGNiaW5kKG1kc19iZXN0X3I2JHBvaW50cywNCiAgICAgICAgICAgICAgICAgc2NvcmVzKGRjYV9yNiwNCiAgICAgICAgICAgICAgICAgICAgICAgIGRpc3BsYXkgPSAic2l0ZXMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgb3JpZ2luID0gVFJVRSlbLCAxOmF4XSkNCg0KZ2djb3JyKGF4aXNfcjYsIA0KICAgICAgIG1ldGhvZD1jKCJldmVyeXRoaW5nIiwia2VuZGFsbCIpLCANCiAgICAgICBsYWJlbCA9IFRSVUUsDQogICAgICAgbGFiZWxfc2l6ZSA9IDMsIA0KICAgICAgIGxhYmVsX2NvbG9yID0gImJsYWNrIiwgIA0KICAgICAgIG5icmVha3MgPSA4LA0KICAgICAgIGxhYmVsX3JvdW5kID0gMywNCiAgICAgICBsb3cgPSAicmVkIiwNCiAgICAgICBtaWQgPSAid2hpdGUiLA0KICAgICAgIGhpZ2ggPSAiZ3JlZW4iKQ0KDQpgYGANCg0KIyMjIyMgU2F2ZSB0aGUgZmlndXJlDQpgYGB7cn0NCmdnc2F2ZShmaWxlbmFtZSA9IGZpbGUucGF0aChkYXRhUGF0aCwib3V0cHV0cy9oaURlbnNfYjE1MDBfY29ycmVsYXRpb25QQ0F2c05NRFNfcjYucG5nIiksDQogICAgICAgZGV2aWNlID0gInBuZyIsDQogICAgICAgZHBpPTMwMCApDQoNCiMgU3dpdGNoaW5nIGRpcmVjdGlvbiBvZiBOTURTMQ0KIyBtZHNfYmVzdCRwb2ludHNbLCAxXSA8LSAtbWRzX2Jlc3QkcG9pbnRzWywgMV0NCmBgYA0KDQojIyMgUGxvdHRpbmcgRENBICYgR05NRFMNCmBgYHtyfQ0KIyMgQWRkaW5nIHNjb3JlcyB0byBkYXRhIGZyYW1lDQpvdHVfNiRnbm1kczEgPC0gbWRzX2Jlc3RfcjYkcG9pbnRzWywgMV0NCm90dV82JGdubWRzMiA8LSBtZHNfYmVzdF9yNiRwb2ludHNbLCAyXQ0Kb3R1XzYkZGNhMSA8LSBzY29yZXMoZGNhX3I2LCBkaXNwbGF5ID0gInNpdGVzIiwgb3JpZ2luID0gVFJVRSlbLCAxXQ0Kb3R1XzYkZGNhMiA8LSBzY29yZXMoZGNhX3I2LCBkaXNwbGF5ID0gInNpdGVzIiwgb3JpZ2luID0gVFJVRSlbLCAyXQ0KDQpwX2dubWRzX3I2IDwtIGdncGxvdChkYXRhID0gb3R1XzYsDQogICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IGdubWRzMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gZ25tZHMyKSkgKw0KICB0aGVtZV9jbGFzc2ljKCkgKw0KICBjb29yZF9maXhlZCgpICsNCiAgZ2d0aXRsZSgiR05NRFMiLA0KICAgICAgICAgIHN1YnRpdGxlID0gIkZpcnN0IHJ1biIpICsNCiAgZ2VvbV9wb2ludChjb2xvdXIgPSAicmVkIikgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLA0KICAgICAgICAgICAgIGxpbmV0eXBlID0gMiwNCiAgICAgICAgICAgICBjb2xvdXIgPSAibGlnaHRncmF5IikgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLA0KICAgICAgICAgICAgIGxpbmV0eXBlID0gMiwNCiAgICAgICAgICAgICBjb2xvdXIgPSAibGlnaHRncmF5IikNCg0KcF9kY2FfcjYgPC0gZ2dwbG90KGRhdGEgPSBvdHVfNiwNCiAgICAgICAgICAgICAgICAgICBhZXMoeCA9IGRjYTEsDQogICAgICAgICAgICAgICAgICAgICAgIHkgPSBkY2EyKSkgKw0KICB0aGVtZV9jbGFzc2ljKCkgKw0KICBjb29yZF9maXhlZCgpICsNCiAgZ2d0aXRsZSgiRENBIiwNCiAgICAgICAgICBzdWJ0aXRsZSA9ICJGaXJzdCBydW4iKSArDQogIGdlb21fcG9pbnQoY29sb3VyID0gInJlZCIpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwNCiAgICAgICAgICAgICBsaW5ldHlwZSA9IDIsDQogICAgICAgICAgICAgY29sb3VyID0gImxpZ2h0Z3JheSIpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwNCiAgICAgICAgICAgICBsaW5ldHlwZSA9IDIsDQogICAgICAgICAgICAgY29sb3VyID0gImxpZ2h0Z3JheSIpDQoNCnBfZ25tZHNfcjYgKyBwX2RjYV9yNg0KDQoNCmBgYA0KDQojIyMjIyBTYXZlIHRoZSBmaWd1cmUNCmBgYHtyfQ0KZ2dzYXZlKGZpbGUucGF0aChkYXRhUGF0aCwib3V0cHV0cy9oaURlbnNfYjE1MDBfZ25tZHNfZGNhX3I2LnBuZyIpLA0KICAgICAgIGRldmljZSA9ICJwbmciLCANCiAgICAgICBkcGk9MzAwKQ0KYGBgDQoNCg0KIyMgU3BlY2llcy1lbnZpcm9ubWVudCByZWxhdGlvbnNoaXBzDQojIyMgU2VsZWN0aW5nIG9yZGluYXRpb24NCmBgYHtyfQ0Kb3JkIDwtIG1kc19iZXN0X3I2DQoNCiMjIEF4aXMgc2NvcmVzIGlmIHNlbGVjdGVkIG9yZCBpcyBHTk1EUw0KYXhpcyA8LSBvcmQkcG9pbnRzICU+JSBhcy5kYXRhLmZyYW1lDQoNCiMjIEF4aXMgc2NvcmVzIGlmIHNlbGVjdGVkIG9yZCBpcyBEQ0ENCiMgYXhpcyA8LSBzY29yZXMob3JkLA0KIyAgICAgICAgICAgICAgICBkaXNwbGF5ID0gInNpdGVzIiwNCiMgICAgICAgICAgICAgICAgb3JpZ2luID0gVFJVRSlbLCAxOmF4XSkNCg0KYGBgDQoNCiMjIyBDcmVhdGUgYWRkaXRpb25hbCB2YXJpYWJsZXMNCmRlY2lkZWQgdG8gbWFrZSBNTEQtYmF0aHkgdmFycw0KYGBge3J9DQplbnY8LWVudiAlPiUgDQogIG11dGF0ZSAoIk1MRG1lYW5fYmF0aHkiPU1MRG1lYW5fUm9iaW5zb24tKGJhdGh5Ki0xKSwNCiAgICAgICAgICAiTUxEbWluX2JhdGh5Ij1NTERtaW5fUm9iaW5zb24tKGJhdGh5Ki0xKSwNCiAgICAgICAgICAiTUxEbWF4X2JhdGh5Ij1NTERtYXhfUm9iaW5zb24tKGJhdGh5Ki0xKSkNCg0KZW52JE1MRG1lYW5fYmF0aHk8LWN1dChlbnYkTUxEbWVhbl9iYXRoeSwgDQogICAgICBicmVha3M9YygtMjU2MCwgLTIwLDIwLDEzMCksI2NoZWNrZWQgcmFuZ2Ugb2YgdmFsdWVzIGZpcnN0IChtaW4gLTI1NTQsIG1heCAxMjMpDQogICAgICBsYWJlbHM9YygnYmVsb3dNTEQnLCdvblB5Y25vJywnaW5NaXhMYXllcicpKQ0KZW52JE1MRG1pbl9iYXRoeTwtY3V0KGVudiRNTERtaW5fYmF0aHksIA0KICAgICAgYnJlYWtzPWMoLTI1NjAsIC0yMCwyMCwxMzApLCNjaGVja2VkIHJhbmdlIG9mIHZhbHVlcyBmaXJzdCAobWluIC0yNTU0LCBtYXggMTIzKQ0KICAgICAgbGFiZWxzPWMoJ2JlbG93TUxEJywnb25QeWNubycsJ2luTWl4TGF5ZXInKSkNCmVudiRNTERtYXhfYmF0aHk8LWN1dChlbnYkTUxEbWF4X2JhdGh5LCANCiAgICAgIGJyZWFrcz1jKC0yNTYwLCAtMjAsMjAsMTMwKSwjY2hlY2tlZCByYW5nZSBvZiB2YWx1ZXMgZmlyc3QgKG1pbiAtMjU1NCwgbWF4IDEyMykNCiAgICAgIGxhYmVscz1jKCdiZWxvd01MRCcsJ29uUHljbm8nLCdpbk1peExheWVyJykpDQoNCmVudiRzd0RlbnNSb2JfYXZzPC1zd1JobyhzYWxpbml0eT1lbnYkU21lYW5fUm9iaW5zb24sDQogICAgICAgICAgICAgICAgICAgICAgICAgdGVtcGVyYXR1cmU9ZW52JFRtZWFuX1JvYmluc29uLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHByZXNzdXJlPShlbnYkYmF0aHkqLTEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGVvcz0idW5lc2NvIikNCg0KYGBgDQoNCg0KDQojIyMgQ29ycmVsYXRpb24gb3JkaW5hdGlvbiBheGVzIGFuZCBlbnZpcm9ubWVudGFsIHZhcmlhYmxlcw0KIyMjIyBSZW1vdmluZyBub24tZW52IHZhcnMNCmBgYHtyfQ0KZW52X2NvbnQ8LWVudiU+JSBzZWxlY3QoLWMobGFuZHNjYXBlLHNlZGNsYXNzLGdtb3JwaCwgTUxEbWVhbl9iYXRoeSwgTUxEbWF4X2JhdGh5LCBNTERtaW5fYmF0aHksICNjYXRlZ29yaWNhbA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgb3B0aW9uYWwsICNub3QgYSB2YXINCiAgICAgICAgICAgICAgICAgICAgICAgICAgIE1MRG1heF9Sb2JpbnNvbiwgTUxEbWVhbl9Sb2JpbnNvbiwgTUxEbWluX1JvYmluc29uLCAgI3JlcGxhY2VkIGJ5IG5ldyB2YXJzDQogICAgICAgICAgICAgICAgICAgICAgICAgICBNTERzZF9Sb2JpbnNvbiAjbm90IG1lYW5pbmdmdWwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICkpDQplbnZfY29udDwtZW52X2NvbnQlPiUgbXV0YXRlX2lmKGlzLmludGVnZXIsYXMubnVtZXJpYykNCmVudl9jb3JyIDwtIGVudl9jb250ICMlPiUgc2VsZWN0KC1jKFNhbXBJRCkpDQoNCiMgZW52X2NvcnIkY29vcmRzLngxPC1hcy5udW1lcmljKGVudl9jb3JyJGNvb3Jkcy54MSkNCiMgZW52X2NvcnIkY29vcmRzLngyPC1hcy5udW1lcmljKGVudl9jb3JyJGNvb3Jkcy54MikNCg0KZW52X2NvcnJbKCFpcy5udW1lcmljKGVudl9jb3JyKSksXQ0KYGBgDQoNCiMjIyMgQ29ycmVsYXRpb25zDQpgYGB7cn0NCiMgVmVjdG9yIHRvIGhvbGQgY29ycmVsYXRpb25zDQpjb3JfYXgxIDwtIE5VTEwNCmNvcl9heDIgPC0gTlVMTA0KcHZfYXgxIDwtIE5VTEwNCnB2X2F4MiA8LSBOVUxMDQoNCiMgTk1EUzENCmZvciggaSBpbiBzZXEobGVuZ3RoKGVudl9jb3JyKSkpIHsNCiAgY3QuaSA8LSBjb3IudGVzdChheGlzJE1EUzEsDQogICAgICAgICAgICAgICAgICAgZW52X2NvcnJbLCBpXSwNCiAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAia2VuZGFsbCIpDQogIGNvcl9heDFbaV0gPC0gY3QuaSRlc3RpbWF0ZQ0KICBwdl9heDFbaV0gPC0gY3QuaSRwLnZhbHVlDQp9DQoNCiMgTk1EUzINCmZvciggaSBpbiBzZXEobGVuZ3RoKGVudl9jb3JyKSkpIHsNCiAgY3QuaSA8LSBjb3IudGVzdChheGlzJE1EUzIsDQogICAgICAgICAgICAgICAgICAgZW52X2NvcnJbLCBpXSwNCiAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAia2VuZGFsbCIpDQogIGNvcl9heDJbaV0gPC0gY3QuaSRlc3RpbWF0ZQ0KICBwdl9heDJbaV0gPC0gY3QuaSRwLnZhbHVlDQp9DQoNCmNvcl90YWIgPC0gZGF0YS5mcmFtZShlbnYgPSBuYW1lcyhlbnZfY29yciksDQogICAgICAgICAgICAgICAgICAgICAgb3JkX2F4MSA9IGNvcl9heDEsDQogICAgICAgICAgICAgICAgICAgICAgcHZhbF9heDEgPSBwdl9heDEsDQogICAgICAgICAgICAgICAgICAgICAgb3JkX2F4MiA9IGNvcl9heDIsDQogICAgICAgICAgICAgICAgICAgICAgcHZhbF9heDIgPSBwdl9heDIpDQoNCmNvcl90YWINCg0Kd3JpdGUuY3N2KHggPSBjb3JfdGFiLA0KICAgICAgICAgIGZpbGUgPSBmaWxlLnBhdGgoZGF0YVBhdGgsImlucHV0cy9TUExJVFMvREVFUC9oaURlbnNfYjE1MDBfY29yLXRhYmxlX3I2XzIwMHJlcF9NTEQtYmF0aHkuY3N2IiksDQogICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpDQpgYGANCiMjIyBEb3QgY2hhcnQgdG8gY2hlY2sgZm9yIGdhcHMgaW4gY29ycmVsYXRpb24NCg0KYGBge3J9DQpjb3JfYTFfc29ydDwtY29yX3RhYiU+JQ0KICBtdXRhdGUoYWJzX29yZF9heDE9YWJzKG9yZF9heDEpLA0KICAgICAgICAgYWJzX29yZF9heDI9YWJzKG9yZF9heDIpKSAlPiUNCiAgYXJyYW5nZShkZXNjKGFic19vcmRfYXgxKSkNCg0KY29yX2EyX3NvcnQ8LWNvcl90YWIlPiUNCiAgbXV0YXRlKGFic19vcmRfYXgxPWFicyhvcmRfYXgxKSwNCiAgICAgICAgIGFic19vcmRfYXgyPWFicyhvcmRfYXgyKSkgJT4lDQogIGFycmFuZ2UoZGVzYyhhYnNfb3JkX2F4MikpDQoNCmRvdGNoYXJ0KGNvcl9hMV9zb3J0JGFic19vcmRfYXgxLCBtYWluPSJBYnNvbHV0ZSAoKy8tKSBjb3JyZWxhdGlvbnMgYmV0d2VlbiBlbnZWYXJzIGFuZCBnbm1kcyBheGlzIDEiKQ0KDQpjb3JfY3V0PC0wLjUwICNkZWNpZGUNCg0KY29yX3NlbDwtc3Vic2V0KGNvcl9hMV9zb3J0LGFic19vcmRfYXgxPmNvcl9jdXQpDQpjb3Jfc2VsDQoNCmFzLmRhdGEuZnJhbWUoY29yX3NlbCRlbnYpDQpgYGANCg0KDQojIyMgU2VsIGVudiB2YXIgKHRvcCBjb3JyKQ0KYGBge3J9DQplbnZfb3MgPC0gZW52WywgY29yX3NlbCRlbnZdDQplbnZfb3MNCnN0cihlbnZfb3MpDQoNCg0KYGBgDQoNCg0KIyMjIE9yZGlzdXJmcyB0b3AgY29ycg0KYGBge3J9DQpvcmRzcmZzIDwtIGxpc3QobGVuZ3RoID0gbmNvbChlbnZfb3MpKQ0KDQpmb3IgKGkgaW4gc2VxKG5jb2woZW52X29zKSkpIHsNCiAgb3MuaSA8LSBnZ19vcmRpc3VyZihvcmQgPSBvcmQsDQogICAgICAgICAgICAgICAgICAgICAgZW52LnZhciA9IGVudl9vc1ssIGldLA0KICAgICAgICAgICAgICAgICAgICAgIHB0LnNpemUgPSAxLA0KICAgICAgICAgICAgICAgICAgICAgICMgYmlud2lkdGggPSAwLjA1LA0KICAgICAgICAgICAgICAgICAgICAgIHZhci5sYWJlbCA9IG5hbWVzKGVudl9vcylbaV0sDQogICAgICAgICAgICAgICAgICAgICAgZ2VuLnRleHQuc2l6ZSA9IDEwLA0KICAgICAgICAgICAgICAgICAgICAgIHRpdGxlLnRleHQuc2l6ZSA9IDE1LA0KICAgICAgICAgICAgICAgICAgICAgIGxlZy50ZXh0LnNpemUgPSAxMA0KICAgICAgICAgICAgICAgICAgICAgICkNCiAgDQogIG9yZHNyZnNbW2ldXSA8LSBvcy5pJHBsb3QNCn0NCg0Kb3Jkc3Jmc19wbHQgPC0gZ2dhcnJhbmdlKHBsb3RsaXN0ID0gb3Jkc3JmcywNCiAgICAgICAgICAgICAgICAgICAgICAgICBucm93ID0gNCwNCiAgICAgICAgICAgICAgICAgICAgICAgICBuY29sID0gMykNCg0Kb3Jkc3Jmc19wbHQNCmBgYA0KIyMjIyMgU2F2ZSBzb21lIG91dHB1dHMNCmBgYHtyfQ0KZ2dleHBvcnQob3Jkc3Jmc19wbHQsDQogICAgICAgICAgZmlsZW5hbWUgPSBmaWxlLnBhdGgoZGF0YVBhdGgsIm91dHB1dHMvaGlEZW5zX2IxNTAwX29yZGlzdXJmc190b3BfY29yci5wbmciKSwNCiAgICAgICAgICB3aWR0aCA9IDEwMDAsDQogICAgICAgICAgaGVpZ2h0ID0gMTAwMCkNCg0KYGBgDQoNCg0KIyMjIFNlbCBlbnYgdmFyIChtYW51YWwpDQpgYGB7cn0NCmVudl9vc19tIDwtIGVudlssYygiVG1lYW5fUm9iaW5zb24iLCAjdG9wIGNvcnINCiAgICAgICAgICAgICAgICAgICJzYWx0X21heCIsICN0b3AgY29ycg0KICAgICAgICAgICAgICAgICAgIlNtYXhfUm9iaW5zb24iLCAjY29tcGFyaXNvbiB0byB0b3AgY29ycg0KICAgICAgICAgICAgICAgICAgInN3RGVuc1JvYl9hdnMiLCAjdG9wIGNvcnINCiAgICAgICAgICAgICAgICAgICJCTzIyX2ljZWNvdmVybHRtYXhfc3MiLCN0b3AgY29yciBheDINCiAgICAgICAgICAgICAgICAgICJCTzIyX2ljZWNvdmVybWVhbl9zcyIsI3RvcCBjb3JyDQogICAgICAgICAgICAgICAgICAiQk8yMl9kaXNzb3htZWFuX2JkbWVhbiIsI3RvcCBjb3JyDQogICAgICAgICAgICAgICAgICAjIkJPMjJfY2FyYm9ucGh5dG9sdG1pbl9iZG1lYW4iLCN0b3AgY29yciAtIG5vIGNsZWFyIGdyYWRpZW50IGluIG9yZGlzdXJmDQogICAgICAgICAgICAgICAgICAiQk8yMl9wcGx0bWluX3NzIiwgI3RvcCBjb3JyDQogICAgICAgICAgICAgICAgICAiWC55IiwgI2NvbXBhcmlzb24gdG8gWQ0KICAgICAgICAgICAgICAgICAgIlkiLCAjdG9wIGNvcnINCiAgICAgICAgICAgICAgICAgICJzcGRfc3RkIiwgI3RvcCBjb3JyIGF4MiAoYmxlbmRlZCBtb2RlbCkNCiAgICAgICAgICAgICAgICAgICJDU3Bkc2RfUm9iaW5zb24iLCAjY29tcGFyaXNvbiB0byB0b3AgY29yciBheDIgKGJsZW5kZWQgbW9kZWwpDQogICAgICAgICAgICAgICAgICAibXVkIiwgI2hpZ2hlc3Qgc2VkIHZhciBheDEgKyBjb3JyDQogICAgICAgICAgICAgICAgICAiZ3JhdmVsIiwjaGlnaGVzdCBzZWQgdmFyIGF4MSAtIGNvcnINCiAgICAgICAgICAgICAgICAgICJCTzIyX3NpbGljYXRlbHRtYXhfYmRtZWFuIiwgI2p1c3QgdW5kZXIgdG9wIGNvcnIgYXgxDQogICAgICAgICAgICAgICAgICAiYmF0aHkiLCAjaW50dWl0aXZlIGZvciBjb21wYXJpc29ucw0KICAgICAgICAgICAgICAgICAgInNsb3BlOSIjaW50dWl0aXZlIGZvciBjb21wYXJpc29ucw0KICAgICAgICAgICAgICAgICldDQplbnZfb3NfbQ0Kc3RyKGVudl9vc19tKQ0KDQpgYGANCg0KDQojIyMgT3JkaXN1cmZzIG1hbnVhbGx5IHNlbGVjdGVkDQpgYGB7cn0NCm9yZHNyZnNfbSA8LSBsaXN0KGxlbmd0aCA9IG5jb2woZW52X29zX20pKQ0KDQpmb3IgKGkgaW4gc2VxKG5jb2woZW52X29zX20pKSkgew0KICBvcy5pX20gPC0gZ2dfb3JkaXN1cmYob3JkID0gb3JkLA0KICAgICAgICAgICAgICAgICAgICAgIGVudi52YXIgPSBlbnZfb3NfbVssIGldLA0KICAgICAgICAgICAgICAgICAgICAgIHB0LnNpemUgPSAxLA0KICAgICAgICAgICAgICAgICAgICAgICMgYmlud2lkdGggPSAwLjA1LA0KICAgICAgICAgICAgICAgICAgICAgIHZhci5sYWJlbCA9IG5hbWVzKGVudl9vc19tKVtpXSwNCiAgICAgICAgICAgICAgICAgICAgICBnZW4udGV4dC5zaXplID0gMTAsDQogICAgICAgICAgICAgICAgICAgICAgdGl0bGUudGV4dC5zaXplID0gMTUsDQogICAgICAgICAgICAgICAgICAgICAgbGVnLnRleHQuc2l6ZSA9IDEwKQ0KICANCiAgb3Jkc3Jmc19tW1tpXV0gPC0gb3MuaV9tJHBsb3QNCn0NCg0Kb3Jkc3Jmc19wbHRfbSA8LSBnZ2FycmFuZ2UocGxvdGxpc3QgPSBvcmRzcmZzX20sDQogICAgICAgICAgICAgICAgICAgICAgICAgbnJvdyA9IDYsDQogICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IDMpDQoNCm9yZHNyZnNfcGx0X20NCmBgYA0KDQojIyMjIyBTYXZlIHNvbWUgb3V0cHV0cw0KYGBge3J9DQpnZ2V4cG9ydChvcmRzcmZzX3BsdF9tLA0KICAgICAgICAgIGZpbGVuYW1lID0gZmlsZS5wYXRoKGRhdGFQYXRoLCJvdXRwdXRzL2hpRGVuc19iMTUwMF9vcmRpc3VyZnNfbWFuX3NlbF9kb21lYW4ucG5nIiksDQogICAgICAgICAgd2lkdGggPSAyMDAwLA0KICAgICAgICAgIGhlaWdodCA9IDIwMDApDQoNCmBgYA0KDQoNCg0KDQojIyMgRW52Zml0DQpgYGB7cn0NCiMjIFNlbGVjdCBpZiBhbnkgdmFyIHNob3VsZCBiZSBleGNsdWRlZCBmcm9tIGVudmZpdCAobWFrZXMgbGVzcyBidXN5IHRvIHJlYWQpDQplbnZfb3NfbV9lbnZmaXQ8LWVudl9vc19tIFssYygiVG1lYW5fUm9iaW5zb24iLCAjdG9wIGNvcnINCiAgICAgICAgICAgICAgICAgICJzYWx0X21heCIsICN0b3AgY29ycg0KICAgICAgICAgICAgICAgICAgIlNtYXhfUm9iaW5zb24iLCAjY29tcGFyaXNvbiB0byB0b3AgY29ycg0KICAgICAgICAgICAgICAgICAgInN3RGVuc1JvYl9hdnMiLCAjdG9wIGNvcnINCiAgICAgICAgICAgICAgICAgICJCTzIyX2ljZWNvdmVybHRtYXhfc3MiLCN0b3AgY29yciBheDINCiAgICAgICAgICAgICAgICAgICMiQk8yMl9pY2Vjb3Zlcm1lYW5fc3MiLCN0b3AgY29ycg0KICAgICAgICAgICAgICAgICAgIkJPMjJfZGlzc294bWVhbl9iZG1lYW4iLCN0b3AgY29ycg0KICAgICAgICAgICAgICAgICAgIyJCTzIyX2Rpc3NveGx0bWluX2JkbWVhbiIsI3RvcCBjb3JyDQogICAgICAgICAgICAgICAgICAjIkJPMjJfY2FyYm9ucGh5dG9sdG1pbl9iZG1lYW4iLCN0b3AgY29yciAtIG5vIGNsZWFyIGdyYWRpZW50IGluIG9yZGlzdXJmDQogICAgICAgICAgICAgICAgICAiQk8yMl9wcGx0bWluX3NzIiwgI3RvcCBjb3JyDQogICAgICAgICAgICAgICAgICAiWC55IiwgI2NvbXBhcmlzb24gdG8gWQ0KICAgICAgICAgICAgICAgICAgIlkiLCAjdG9wIGNvcnINCiAgICAgICAgICAgICAgICAgICJzcGRfc3RkIiwgI3RvcCBjb3JyIGF4MiAoYmxlbmRlZCBtb2RlbCkNCiAgICAgICAgICAgICAgICAgIyAiQ1NwZHNkX1JvYmluc29uIiwgI2NvbXBhcmlzb24gdG8gdG9wIGNvcnIgYXgyIChibGVuZGVkIG1vZGVsKQ0KICAgICAgICAgICAgICAgICAgIm11ZCIsICNoaWdoZXN0IHNlZCB2YXIgYXgxICsgY29ycg0KICAgICAgICAgICAgICAgICAgImdyYXZlbCIsI2hpZ2hlc3Qgc2VkIHZhciBheDEgLSBjb3JyDQogICAgICAgICAgICAgICAgICAiQk8yMl9zaWxpY2F0ZWx0bWF4X2JkbWVhbiIsICNqdXN0IHVuZGVyIHRvcCBjb3JyIGF4MQ0KICAgICAgICAgICAgICAgICAgImJhdGh5IiAjaW50dWl0aXZlIGZvciBjb21wYXJpc29ucw0KICAgICAgICAgICAgICAgIA0KICANCildDQoNCmNvbG5hbWVzKGVudl9vc19tX2VudmZpdCk8LWMoIlQiLCAjdG9wIGNvcnINCiAgICAgICAgICAgICAgICAgICJzTXgiLCAjdG9wIGNvcnINCiAgICAgICAgICAgICAgICAgICJTbWF4UiIsICNjb21wYXJpc29uIHRvIHRvcCBjb3JyDQogICAgICAgICAgICAgICAgICAic3dEZW5zUiIsICN0b3AgY29ycg0KICAgICAgICAgICAgICAgICAgImljZWNvdm1heCIsI3RvcCBjb3JyIGF4Mg0KICAgICAgICAgICAgICAgICAgIyJpY2Vjb3ZhdiIsI3RvcCBjb3JyDQogICAgICAgICAgICAgICAgICAiZGlzc294YXYiLCN0b3AgY29ycg0KICAgICAgICAgICAgICAgICAgIyJkaXNzb3htaW4iLCN0b3AgY29ycg0KICAgICAgICAgICAgICAgICAgIyJCTzIyX2NhcmJvbnBoeXRvbHRtaW5fYmRtZWFuIiwjdG9wIGNvcnIgLSBubyBjbGVhciBncmFkaWVudCBpbiBvcmRpc3VyZg0KICAgICAgICAgICAgICAgICAgInBwbHRtaW4iLCAjdG9wIGNvcnINCiAgICAgICAgICAgICAgICAgICJYIiwgI2NvbXBhcmlzb24gdG8gWQ0KICAgICAgICAgICAgICAgICAgIlkiLCAjdG9wIGNvcnINCiAgICAgICAgICAgICAgICAgICJzcGRfc3RkIiwgI3RvcCBjb3JyIGF4MiAoYmxlbmRlZCBtb2RlbCkNCiAgICAgICAgICAgICAgICAgIyAiQ1NzZCIsICNjb21wYXJpc29uIHRvIHRvcCBjb3JyIGF4MiAoYmxlbmRlZCBtb2RlbCkNCiAgICAgICAgICAgICAgICAgICJtdWQiLCAjaGlnaGVzdCBzZWQgdmFyIGF4MSArIGNvcnINCiAgICAgICAgICAgICAgICAgICJncmF2ZWwiLCNoaWdoZXN0IHNlZCB2YXIgYXgxIC0gY29ycg0KICAgICAgICAgICAgICAgICAgIlNpTHRtYXgiLCAjanVzdCB1bmRlciB0b3AgY29yciBheDENCiAgICAgICAgICAgICAgICAgICJiYXRoeSIgI2ludHVpdGl2ZSBmb3IgY29tcGFyaXNvbnMNCiAgICAgICAgICAgICAgICAgKQ0KDQojIyBFbnZmb3QgcGxvdA0KZ2dfZW52Zml0KG9yZCA9IG9yZCwNCiAgICAgICAgICBlbnYgPSBlbnZfb3NfbV9lbnZmaXQsDQogICAgICAgICAgcHQuc2l6ZSA9IDEpDQoNCiMjIEVudmZpdCBhbmFseXNpcw0KDQplZiA8LSBlbnZmaXQob3JkID0gb3JkLA0KICAgICAgICAgICAgIGVudiA9IGVudl9vc19tX2VudmZpdCwNCiAgICAgICAgICAgICAjIG5hLnJtID0gVFJVRQ0KICAgICAgICAgICAgICkNCmVmREYgPC0gYXMuZGF0YS5mcmFtZShzY29yZXMoZWYsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3BsYXkgPSAidmVjdG9ycyIpKQ0KYGBgDQoNCiMjIyMjIFNhdmUgdGhlIHBsb3QNCmBgYHtyfQ0KZ2dzYXZlKGZpbGVuYW1lID0gZmlsZS5wYXRoKGRhdGFQYXRoLCJvdXRwdXRzL2hpRGVuc19iMTUwMF9FbnZGaXRfbWFuX3NlbF9jbG5fZG9tZWFuLnBuZyIpLA0KICAgICAgIGRldmljZSA9ICJwbmciLA0KICAgICAgIGRwaT0zMDAgKQ0KYGBgDQojIyMgQ2F0ZWdvcmljYWwgZW52VmFyIHZpc3VhbGlzZWQgb24gdGhlIG1kc3Bsb3RzDQoNCiMjIyMgdXNlIGRhdGFzZXQgd2l0aCBjYXRlZ29yaWNhbCB2YXIgaW5jbHVkZWQNCg0KYGBge3J9DQplbnZfdmlzPC1lbnYNCmVudl92aXMkZ25tZHMxIDwtIG90dV82JGdubWRzMQ0KZW52X3ZpcyRnbm1kczIgPC0gb3R1XzYkZ25tZHMyDQoNCmVudl92aXMkZGNhMSA8LSBvdHVfNiRkY2ExDQplbnZfdmlzJGRjYTIgPC0gb3R1XzYkZGNhMg0KDQplbnZfdmlzJFNhbXBJRCA8LSBlbnZTZWwkU2FtcElEDQpgYGANCg0KIyMjIyBnbm1kcyB3IG1sZCBtZWFuIC0gYmF0aHkNCmBgYHtyfQ0KZGNhX2ludCA8LSBnZ3Bsb3QoZGF0YSA9IGVudl92aXMsDQogICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IGRjYTEsDQogICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGRjYTIpKSArDQogIHRoZW1lX2NsYXNzaWMoKSArDQogIGNvb3JkX2ZpeGVkKCkgKw0KICBnZ3RpdGxlKCJJbnRlcmFjdGl2ZSBEQ0Egc2FtcGxlIElEIHBsb3QiLA0KICAgICAgICAgIHN1YnRpdGxlID0gIkZpcnN0IHJ1biIpICsNCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gZmFjdG9yKFNhbXBJRCkpKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsDQogICAgICAgICAgICAgbGluZXR5cGUgPSAyLA0KICAgICAgICAgICAgIGNvbG91ciA9ICJsaWdodGdyYXkiKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsDQogICAgICAgICAgICAgbGluZXR5cGUgPSAyLA0KICAgICAgICAgICAgIGNvbG91ciA9ICJsaWdodGdyYXkiKQ0KDQpnZ3Bsb3RseShkY2FfaW50KQ0KYGBgDQoNCg0KDQoNCg0KIyMjIyBnbm1kcyB3IG1sZCBtZWFuIC0gYmF0aHkNCmBgYHtyfQ0KcF9tbGQgPC0gZ2dwbG90KGRhdGEgPSBlbnZfdmlzLA0KICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBnbm1kczEsDQogICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGdubWRzMikpICsNCiAgdGhlbWVfY2xhc3NpYygpICsNCiAgY29vcmRfZml4ZWQoKSArDQogIGdndGl0bGUoIkdOTURTIGNvbG91cmVkIGJ5IHByb3hpbWl0eSB0byBtaXhlZCBsYXllciBkZXB0aCIsDQogICAgICAgICAgc3VidGl0bGUgPSAiRmlyc3QgcnVuIikgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBNTERtZWFuX2JhdGh5KSkgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLA0KICAgICAgICAgICAgIGxpbmV0eXBlID0gMiwNCiAgICAgICAgICAgICBjb2xvdXIgPSAibGlnaHRncmF5IikgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLA0KICAgICAgICAgICAgIGxpbmV0eXBlID0gMiwNCiAgICAgICAgICAgICBjb2xvdXIgPSAibGlnaHRncmF5IikNCg0KcF9tbGQNCmBgYA0KDQojIyMjIGdubWRzIHcgc2VkY2xhc3MNCiMjIyMjIGRlYWwgd2l0aCBzZWRjbGFzcyBjb2Rlcw0KYGBge3J9DQplbnZfdmlzPC0gZW52X3ZpcyAlPiUgDQogIG11dGF0ZSgNCiAgICBzZWRjbGFzc05hbWUgPSBjYXNlX3doZW4oDQogICAgICAgICAgICBzZWRjbGFzcyA9PSAiMSIgfiAiU2VkQ292ZXJSIiwNCiAgICAgICAgICAgIHNlZGNsYXNzID09ICI1IiB+ICJSb2NrIiwNCiAgICAgICAgICAgIHNlZGNsYXNzID09ICIyMCIgfiAiTXVkIiwNCiAgICAgICAgICAgIHNlZGNsYXNzID09ICIyMSIgfiAiTXdCbG9jayIsDQogICAgICAgICAgICBzZWRjbGFzcyA9PSAiNDAiIH4gInNNdWQiLA0KICAgICAgICAgICAgc2VkY2xhc3MgPT0gIjgwIiB+ICJtU2FuZCIsDQogICAgICAgICAgICBzZWRjbGFzcyA9PSAiMTAwIiB+ICJTYW5kIiwNCiAgICAgICAgICAgIHNlZGNsYXNzID09ICIxMTAiIH4gImdNdWQiLA0KICAgICAgICAgICAgc2VkY2xhc3MgPT0gIjExNSIgfiAiZ3NNdWQiLA0KICAgICAgICAgICAgc2VkY2xhc3MgPT0gIjEyMCIgfiAiZ21TYW5kIiwNCiAgICAgICAgICAgIHNlZGNsYXNzID09ICIxMzAiIH4gImdTYW5kIiwNCiAgICAgICAgICAgIHNlZGNsYXNzID09ICIxNTAiIH4gIk1TRyIsDQogICAgICAgICAgICBzZWRjbGFzcyA9PSAiMTYwIiB+ICJzR3JhdmVsIiwNCiAgICAgICAgICAgIHNlZGNsYXNzID09ICIxNzAiIH4gIkdyYXZlbCIsDQogICAgICAgICAgICBzZWRjbGFzcyA9PSAiMTc1IiB+ICJHcmF2QmxvY2siLA0KICAgICAgICAgICAgc2VkY2xhc3MgPT0gIjE4NSIgfiAiU0dCbWl4IiwNCiAgICAgICAgICAgIHNlZGNsYXNzID09ICIyMDUiIH4gIlMvTXdCIiwNCiAgICAgICAgICAgIHNlZGNsYXNzID09ICIyMDYiIH4gIlMvTXdHL0IiLA0KICAgICAgICAgICAgc2VkY2xhc3MgPT0gIjIxNSIgfiAiU0dCYWx0IiwNCiAgICAgICAgICAgIHNlZGNsYXNzID09ICIzMDAiIH4gIkhhcmRTZWQiLA0KICAgICAgICAgICAgc2VkY2xhc3MgPT0gIjUwMCIgfiAiQmlvZ2VuaWMiDQogICAgICAgICAgICAgICAgICAgICAgICANCiAgICApDQogICkNCmBgYA0KDQojIyMjIyMgY29sb3VyIHBhbGV0dGUgdG8gY29wZSB3aXRoIHVwIHRvIDI1IGNhdGVnb3JpY2FsIGNvbG91cnMNCmBgYHtyfQ0KYzI1IDwtIGMoDQogICJkb2RnZXJibHVlMiIsICIjRTMxQTFDIiwgIyByZWQNCiAgImdyZWVuNCIsDQogICIjNkEzRDlBIiwgIyBwdXJwbGUNCiAgIiNGRjdGMDAiLCAjIG9yYW5nZQ0KICAiYmxhY2siLCAiZ29sZDEiLA0KICAic2t5Ymx1ZTIiLCAiI0ZCOUE5OSIsICMgbHQgcGluaw0KICAicGFsZWdyZWVuMiIsDQogICIjQ0FCMkQ2IiwgIyBsdCBwdXJwbGUNCiAgIiNGREJGNkYiLCAjIGx0IG9yYW5nZQ0KICAiZ3JheTcwIiwgImtoYWtpMiIsDQogICJtYXJvb24iLCAib3JjaGlkMSIsICJoaURlbnNfYjE1MDBwaW5rMSIsICJibHVlMSIsICJzdGVlbGJsdWU0IiwNCiAgImRhcmt0dXJxdW9pc2UiLCAiZ3JlZW4xIiwgInllbGxvdzQiLCAieWVsbG93MyIsDQogICJkYXJrb3JhbmdlNCIsICJicm93biINCikNCmBgYA0KDQoNCg0KDQpgYGB7cn0NCg0KcF9zZWQgPC0gZ2dwbG90KGRhdGEgPSBlbnZfdmlzLA0KICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBnbm1kczEsDQogICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGdubWRzMikpICsNCiAgdGhlbWVfY2xhc3NpYygpICsNCiAgY29vcmRfZml4ZWQoKSArDQogIGdndGl0bGUoIkdOTURTIGNvbG91cmVkIGJ5IHNlZGltZW50IGNsYXNzIiwNCiAgICAgICAgICBzdWJ0aXRsZSA9ICJGaXJzdCBydW4iKSArDQogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IGZhY3RvcihzZWRjbGFzc05hbWUpKSkgKw0KICAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXM9YzI1KSsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwNCiAgICAgICAgICAgICBsaW5ldHlwZSA9IDIsDQogICAgICAgICAgICAgY29sb3VyID0gImxpZ2h0Z3JheSIpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwNCiAgICAgICAgICAgICBsaW5ldHlwZSA9IDIsDQogICAgICAgICAgICAgY29sb3VyID0gImxpZ2h0Z3JheSIpKw0KICBndWlkZXMoY29sb3VyPWd1aWRlX2xlZ2VuZChuY29sPTIpKQ0KDQpwX3NlZA0KYGBgDQoNCiMjIyMgZ25tZHMgdyBsYW5zY2FwZQ0KIyMjIyMgZGVhbCB3aXRoIHNlZGNsYXNzIGNvZGVzDQpgYGB7cn0NCmVudl92aXM8LSBlbnZfdmlzICU+JSANCiAgbXV0YXRlKA0KICAgIGxhbmRzY2FwZU5hbWUgPSBjYXNlX3doZW4oDQogICAgICAgICAgICBsYW5kc2NhcGUgPT0gIjEiIH4gIlN0cmFuZGZsYXQiLA0KICAgICAgICAgICAgbGFuZHNjYXBlID09ICIyMSIgfiAiQ29udFNsb3BlIiwNCiAgICAgICAgICAgIGxhbmRzY2FwZSA9PSAiMjIiIH4gIkNhbnlvbiIsDQogICAgICAgICAgICBsYW5kc2NhcGUgPT0gIjMxIiB+ICJWYWxsZXkiLA0KICAgICAgICAgICAgbGFuZHNjYXBlID09ICIzMiIgfiAiRmpvcmQiLA0KICAgICAgICAgICAgbGFuZHNjYXBlID09ICI0MSIgfiAiRGVlcFNlYVBsYW5lIiwNCiAgICAgICAgICAgIGxhbmRzY2FwZSA9PSAiNDIiIH4gIlNsb3BlUGxhaW4iLA0KICAgICAgICAgICAgbGFuZHNjYXBlID09ICI0MyIgfiAiU2hlbGZQbGFpbiIsDQogICAgICAgICAgICBsYW5kc2NhcGUgPT0gIjQzMSIgfiAic2hhbGxvd1ZhbGxleSINCiAgICApDQogICkNCmBgYA0KDQoNCmBgYHtyfQ0KDQpwX2xhbmQgPC0gZ2dwbG90KGRhdGEgPSBlbnZfdmlzLA0KICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBnbm1kczEsDQogICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGdubWRzMikpICsNCiAgdGhlbWVfY2xhc3NpYygpICsNCiAgY29vcmRfZml4ZWQoKSArDQogIGdndGl0bGUoIkdOTURTIGNvbG91cmVkIGJ5IGxhbmRzY2FwZSBjbGFzcyIsDQogICAgICAgICAgc3VidGl0bGUgPSAiRmlyc3QgcnVuIikgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBmYWN0b3IobGFuZHNjYXBlTmFtZSkpKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsDQogICAgICAgICAgICAgbGluZXR5cGUgPSAyLA0KICAgICAgICAgICAgIGNvbG91ciA9ICJsaWdodGdyYXkiKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsDQogICAgICAgICAgICAgbGluZXR5cGUgPSAyLA0KICAgICAgICAgICAgIGNvbG91ciA9ICJsaWdodGdyYXkiKSsNCiAgZ3VpZGVzKGNvbG91cj1ndWlkZV9sZWdlbmQobmNvbD0yKSkNCg0KcF9sYW5kDQpgYGANCg0KDQojIyMjIEdubWRzIHcgZ21vcnBoDQpgYGB7cn0NCg0KcF9nbW8gPC0gZ2dwbG90KGRhdGEgPSBlbnZfdmlzLA0KICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBnbm1kczEsDQogICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGdubWRzMikpICsNCiAgdGhlbWVfY2xhc3NpYygpICsNCiAgY29vcmRfZml4ZWQoKSArDQogIGdndGl0bGUoIkdOTURTIGNvbG91cmVkIGJ5IGxhbmRzY2FwZSBjbGFzcyIsDQogICAgICAgICAgc3VidGl0bGUgPSAiRmlyc3QgcnVuIikgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBmYWN0b3IoZ21vcnBoKSkpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwNCiAgICAgICAgICAgICBsaW5ldHlwZSA9IDIsDQogICAgICAgICAgICAgY29sb3VyID0gImxpZ2h0Z3JheSIpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwNCiAgICAgICAgICAgICBsaW5ldHlwZSA9IDIsDQogICAgICAgICAgICAgY29sb3VyID0gImxpZ2h0Z3JheSIpKw0KICBndWlkZXMoY29sb3VyPWd1aWRlX2xlZ2VuZChuY29sPTIpKQ0KDQpwX2dtbw0KYGBgDQoNCg0KYGBge3J9DQpjYXRfdmFyX3Bsb3RzPC1wX21sZCtwX3NlZCtwX2xhbmQrcF9nbW8NCmBgYA0KIyMjIyMgU2F2ZSB0aGUgcGxvdA0KYGBge3J9DQpnZ2V4cG9ydChjYXRfdmFyX3Bsb3RzLA0KICAgICAgICAgIGZpbGVuYW1lID0gZmlsZS5wYXRoKGRhdGFQYXRoLCJvdXRwdXRzL2hpRGVuc19iMTUwMF9nbm1kc19jYXR2YXIucG5nIiksDQogICAgICAgICAgd2lkdGggPSAxMDAwLA0KICAgICAgICAgIGhlaWdodCA9IDgwMCkNCmBgYA0KDQojIyMjIFNhbXBsZSBpZGVudGlmaWNhdGlvbiBpbiB0aGUgbWRzcGxvdA0KDQpgYGB7cn0NCnBfZ21vIDwtIGdncGxvdChkYXRhID0gZW52X3ZpcywNCiAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gZ25tZHMxLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBnbm1kczIpKSArDQogIHRoZW1lX2NsYXNzaWMoKSArDQogIGNvb3JkX2ZpeGVkKCkgKw0KICBnZ3RpdGxlKCJHTk1EUyBjb2xvdXJlZCBieSBzYW1wbGUiLA0KICAgICAgICAgIHN1YnRpdGxlID0gIkZpcnN0IHJ1biIpICsNCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gZmFjdG9yKFNhbXBJRCkpKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsDQogICAgICAgICAgICAgbGluZXR5cGUgPSAyLA0KICAgICAgICAgICAgIGNvbG91ciA9ICJsaWdodGdyYXkiKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsDQogICAgICAgICAgICAgbGluZXR5cGUgPSAyLA0KICAgICAgICAgICAgIGNvbG91ciA9ICJsaWdodGdyYXkiKSsNCiAgZ3VpZGVzKGNvbG91cj0ibm9uZSIsIHNpemU9Im5vbmUiKQ0KDQpnZ3Bsb3RseShwX2dtbykNCmBgYA0KDQo=